diff --git a/components/tripInfo/NetListTable.tsx b/components/tripInfo/NetListTable.tsx index 1e0146c..6795366 100644 --- a/components/tripInfo/NetListTable.tsx +++ b/components/tripInfo/NetListTable.tsx @@ -1,30 +1,191 @@ import { IconSymbol } from "@/components/ui/icon-symbol"; import React, { useRef, useState } from "react"; import { Animated, Text, TouchableOpacity, View } from "react-native"; +import NetDetailModal from "./modal/NetDetailModal"; import styles from "./style/NetListTable.styles"; // --------------------------- // 🧩 Interface // --------------------------- +interface FishCatch { + fish_species_id: number; + fish_name: string; + catch_number: number; + catch_unit: string; + fish_size: number; + fish_rarity: number; + fish_condition: string; + gear_usage: string; +} + interface NetItem { id: string; stt: string; trangThai: string; + thoiGianBatDau?: string; + thoiGianKetThuc?: string; + viTriHaThu?: string; + viTriThuLuoi?: string; + doSauHaThu?: string; + doSauThuLuoi?: string; + catchList?: FishCatch[]; + ghiChu?: string; } // --------------------------- // 🧵 Dữ liệu mẫu // --------------------------- const data: NetItem[] = [ - { id: "1", stt: "Mẻ 3", trangThai: "Đã hoàn thành" }, - { id: "2", stt: "Mẻ 2", trangThai: "Đã hoàn thành" }, - { id: "3", stt: "Mẻ 1", trangThai: "Đã hoàn thành" }, + { + id: "1", + stt: "Mẻ 3", + trangThai: "Đã hoàn thành", + thoiGianBatDau: "08:00 - 01/11/2025", + thoiGianKetThuc: "12:30 - 01/11/2025", + viTriHaThu: "16°12'34\"N 107°56'12\"E", + viTriThuLuoi: "16°13'45\"N 107°57'23\"E", + doSauHaThu: "45m", + doSauThuLuoi: "48m", + catchList: [ + { + fish_species_id: 3, + fish_name: "Cá chim trắng", + catch_number: 978, + catch_unit: "kg", + fish_size: 111, + fish_rarity: 2, + fish_condition: "Chết", + gear_usage: "", + }, + { + fish_species_id: 13, + fish_name: "Cá song đỏ", + catch_number: 1061, + catch_unit: "kg", + fish_size: 154, + fish_rarity: 2, + fish_condition: "Còn sống", + gear_usage: "", + }, + { + fish_species_id: 15, + fish_name: "Cá hồng", + catch_number: 613, + catch_unit: "kg", + fish_size: 199, + fish_rarity: 2, + fish_condition: "Còn sống", + gear_usage: "", + }, + ], + ghiChu: "Thời tiết tốt, sản lượng cao", + }, + { + id: "2", + stt: "Mẻ 2", + trangThai: "Đã hoàn thành", + thoiGianBatDau: "14:00 - 31/10/2025", + thoiGianKetThuc: "18:45 - 31/10/2025", + viTriHaThu: "16°10'20\"N 107°54'30\"E", + viTriThuLuoi: "16°11'30\"N 107°55'40\"E", + doSauHaThu: "40m", + doSauThuLuoi: "42m", + catchList: [ + { + fish_species_id: 2, + fish_name: "Cá nục", + catch_number: 1102, + catch_unit: "kg", + fish_size: 12, + fish_rarity: 1, + fish_condition: "Bị thương", + gear_usage: "", + }, + { + fish_species_id: 11, + fish_name: "Cá ngừ đại dương", + catch_number: 828, + catch_unit: "kg", + fish_size: 120, + fish_rarity: 1, + fish_condition: "Chết", + gear_usage: "", + }, + ], + ghiChu: "Biển động nhẹ", + }, + { + id: "3", + stt: "Mẻ 1", + trangThai: "Đã hoàn thành", + thoiGianBatDau: "06:30 - 31/10/2025", + thoiGianKetThuc: "11:00 - 31/10/2025", + viTriHaThu: "16°08'15\"N 107°52'45\"E", + viTriThuLuoi: "16°09'25\"N 107°53'55\"E", + doSauHaThu: "35m", + doSauThuLuoi: "38m", + catchList: [ + { + fish_species_id: 6, + fish_name: "Cá mú trắng", + catch_number: 1620, + catch_unit: "kg", + fish_size: 75, + fish_rarity: 2, + fish_condition: "Chết", + gear_usage: "", + }, + { + fish_species_id: 8, + fish_name: "Cá hồng phớn", + catch_number: 648, + catch_unit: "kg", + fish_size: 25, + fish_rarity: 3, + fish_condition: "Còn sống", + gear_usage: "Lưới rê", + }, + { + fish_species_id: 9, + fish_name: "Cá hổ Napoleon", + catch_number: 1111, + catch_unit: "kg", + fish_size: 86, + fish_rarity: 4, + fish_condition: "Bị thương", + gear_usage: "Lưới rê", + }, + { + fish_species_id: 17, + fish_name: "Cá nược", + catch_number: 1081, + catch_unit: "kg", + fish_size: 195, + fish_rarity: 1, + fish_condition: "Chết", + gear_usage: "", + }, + { + fish_species_id: 18, + fish_name: "Cá đuối quạt", + catch_number: 1198, + catch_unit: "kg", + fish_size: 21, + fish_rarity: 4, + fish_condition: "Chết", + gear_usage: "Câu tay", + }, + ], + ghiChu: "Mẻ lưới đầu tiên, sản lượng tốt", + }, ]; const NetListTable: React.FC = () => { const [collapsed, setCollapsed] = useState(true); const [contentHeight, setContentHeight] = useState(0); const animatedHeight = useRef(new Animated.Value(0)).current; + const [modalVisible, setModalVisible] = useState(false); + const [selectedNet, setSelectedNet] = useState(null); const tongSoMe = data.length; const handleToggle = () => { @@ -38,7 +199,11 @@ const NetListTable: React.FC = () => { }; const handleStatusPress = (id: string) => { - console.log(`ID mẻ lưới: ${id}`); + const net = data.find((item) => item.id === id); + if (net) { + setSelectedNet(net); + setModalVisible(true); + } }; return ( @@ -115,6 +280,13 @@ const NetListTable: React.FC = () => { ))} + + {/* Modal chi tiết */} + setModalVisible(false)} + netData={selectedNet} + /> ); }; diff --git a/components/tripInfo/modal/NetDetailModal.tsx b/components/tripInfo/modal/NetDetailModal.tsx new file mode 100644 index 0000000..a2753ce --- /dev/null +++ b/components/tripInfo/modal/NetDetailModal.tsx @@ -0,0 +1,515 @@ +import { IconSymbol } from "@/components/ui/icon-symbol"; +import React, { useState } from "react"; +import { + Modal, + ScrollView, + Text, + TextInput, + TouchableOpacity, + View, +} from "react-native"; +import styles from "./style/NetDetailModal.styles"; + +// --------------------------- +// 🧩 Interface +// --------------------------- +interface FishCatch { + fish_species_id: number; + fish_name: string; + catch_number: number; + catch_unit: string; + fish_size: number; + fish_rarity: number; + fish_condition: string; + gear_usage: string; +} + +interface NetDetail { + id: string; + stt: string; + trangThai: string; + thoiGianBatDau?: string; + thoiGianKetThuc?: string; + viTriHaThu?: string; + viTriThuLuoi?: string; + doSauHaThu?: string; + doSauThuLuoi?: string; + catchList?: FishCatch[]; + ghiChu?: string; +} + +interface NetDetailModalProps { + visible: boolean; + onClose: () => void; + netData: NetDetail | null; +} + +// --------------------------- +// 🧵 Component Modal +// --------------------------- +const NetDetailModal: React.FC = ({ + visible, + onClose, + netData, +}) => { + const [isEditing, setIsEditing] = useState(false); + const [editableCatchList, setEditableCatchList] = useState([]); + const [selectedFishIndex, setSelectedFishIndex] = useState( + null + ); + const [selectedUnitIndex, setSelectedUnitIndex] = useState( + null + ); + const [selectedConditionIndex, setSelectedConditionIndex] = useState< + number | null + >(null); + + // Khởi tạo dữ liệu khi netData thay đổi + React.useEffect(() => { + if (netData?.catchList) { + setEditableCatchList(netData.catchList); + } + }, [netData]); + + if (!netData) return null; + + const isCompleted = netData.trangThai === "Đã hoàn thành"; + + // Danh sách tên cá có sẵn + const fishNameOptions = [ + "Cá chim trắng", + "Cá song đỏ", + "Cá hồng", + "Cá nục", + "Cá ngừ đại dương", + "Cá mú trắng", + "Cá hồng phớn", + "Cá hổ Napoleon", + "Cá nược", + "Cá đuối quạt", + ]; + + // Danh sách đơn vị + const unitOptions = ["kg", "con", "tấn"]; + + // Danh sách tình trạng + const conditionOptions = ["Còn sống", "Chết", "Bị thương"]; + + const handleEdit = () => { + setIsEditing(!isEditing); + }; + + const handleSave = () => { + setIsEditing(false); + // TODO: Save data to backend + console.log("Saved catch list:", editableCatchList); + }; + + const handleCancel = () => { + setIsEditing(false); + setEditableCatchList(netData.catchList || []); + }; + + const updateCatchItem = ( + index: number, + field: keyof FishCatch, + value: string | number + ) => { + setEditableCatchList((prev) => + prev.map((item, i) => { + if (i === index) { + const updatedItem = { ...item }; + if ( + field === "catch_number" || + field === "fish_size" || + field === "fish_rarity" + ) { + updatedItem[field] = Number(value) || 0; + } else { + updatedItem[field] = value as never; + } + return updatedItem; + } + return item; + }) + ); + }; + + const totalCatch = editableCatchList.reduce( + (sum, item) => sum + item.catch_number, + 0 + ); + + const infoItems = [ + { label: "Số thứ tự", value: netData.stt }, + { + label: "Trạng thái", + value: netData.trangThai, + isStatus: true, + }, + { + label: "Thời gian bắt đầu", + value: netData.thoiGianBatDau || "Chưa cập nhật", + }, + { + label: "Thời gian kết thúc", + value: netData.thoiGianKetThuc || "Chưa cập nhật", + }, + { + label: "Vị trí hạ thu", + value: netData.viTriHaThu || "Chưa cập nhật", + }, + { + label: "Vị trí thu lưới", + value: netData.viTriThuLuoi || "Chưa cập nhật", + }, + { + label: "Độ sâu hạ thu", + value: netData.doSauHaThu || "Chưa cập nhật", + }, + { + label: "Độ sâu thu lưới", + value: netData.doSauThuLuoi || "Chưa cập nhật", + }, + ]; + + return ( + + + {/* Header */} + + Chi tiết mẻ lưới + + {isEditing ? ( + <> + + Hủy + + + Lưu + + + ) : ( + + + + + + )} + + + + + + + + + {/* Content */} + + {/* Thông tin chung */} + + {infoItems.map((item, index) => ( + + {item.label} + {item.isStatus ? ( + + + {item.value} + + + ) : ( + {item.value} + )} + + ))} + + + {/* Danh sách cá bắt được */} + + Danh sách cá bắt được + + Tổng: {totalCatch.toLocaleString()} kg + + + + {editableCatchList.map((fish, index) => ( + + {/* Tên cá - Select */} + + Tên cá + {isEditing ? ( + + + setSelectedFishIndex( + selectedFishIndex === index ? null : index + ) + } + > + + {fish.fish_name} + + + + {selectedFishIndex === index && ( + + {fishNameOptions.map((option, optIndex) => ( + { + updateCatchItem(index, "fish_name", option); + setSelectedFishIndex(null); + }} + > + {option} + + ))} + + )} + + ) : ( + {fish.fish_name} + )} + + + {/* Số lượng & Đơn vị */} + + + Số lượng + {isEditing ? ( + + updateCatchItem(index, "catch_number", value) + } + keyboardType="numeric" + placeholder="0" + /> + ) : ( + {fish.catch_number} + )} + + + Đơn vị + {isEditing ? ( + + + setSelectedUnitIndex( + selectedUnitIndex === index ? null : index + ) + } + > + + {fish.catch_unit} + + + + {selectedUnitIndex === index && ( + + {unitOptions.map((option, optIndex) => ( + { + updateCatchItem(index, "catch_unit", option); + setSelectedUnitIndex(null); + }} + > + {option} + + ))} + + )} + + ) : ( + {fish.catch_unit} + )} + + + + {/* Kích thước & Độ hiếm */} + + + Kích thước (cm) + {isEditing ? ( + + updateCatchItem(index, "fish_size", value) + } + keyboardType="numeric" + placeholder="0" + /> + ) : ( + {fish.fish_size} cm + )} + + + Độ hiếm + {isEditing ? ( + + updateCatchItem(index, "fish_rarity", value) + } + keyboardType="numeric" + placeholder="1-5" + /> + ) : ( + {fish.fish_rarity} + )} + + + + {/* Tình trạng */} + + Tình trạng + {isEditing ? ( + + + setSelectedConditionIndex( + selectedConditionIndex === index ? null : index + ) + } + > + + {fish.fish_condition} + + + + {selectedConditionIndex === index && ( + + {conditionOptions.map((option, optIndex) => ( + { + updateCatchItem(index, "fish_condition", option); + setSelectedConditionIndex(null); + }} + > + {option} + + ))} + + )} + + ) : ( + {fish.fish_condition} + )} + + + {/* Ngư cụ sử dụng */} + + Ngư cụ sử dụng + {isEditing ? ( + + updateCatchItem(index, "gear_usage", value) + } + placeholder="Nhập ngư cụ..." + /> + ) : ( + + {fish.gear_usage || "Không có"} + + )} + + + ))} + + {/* Ghi chú */} + {netData.ghiChu && ( + + + Ghi chú + {netData.ghiChu} + + + )} + + + + ); +}; + +export default NetDetailModal; diff --git a/components/tripInfo/modal/style/NetDetailModal.styles.ts b/components/tripInfo/modal/style/NetDetailModal.styles.ts new file mode 100644 index 0000000..90d48ee --- /dev/null +++ b/components/tripInfo/modal/style/NetDetailModal.styles.ts @@ -0,0 +1,236 @@ +import { StyleSheet } from "react-native"; + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: "#f5f5f5", + }, + header: { + flexDirection: "row", + justifyContent: "space-between", + alignItems: "center", + paddingHorizontal: 20, + paddingTop: 30, + paddingBottom: 16, + backgroundColor: "#fff", + borderBottomWidth: 1, + borderBottomColor: "#eee", + }, + title: { + fontSize: 22, + fontWeight: "700", + color: "#000", + flex: 1, + }, + closeButton: { + padding: 4, + }, + closeIconButton: { + backgroundColor: "#FF3B30", + borderRadius: 10, + padding: 10, + justifyContent: "center", + alignItems: "center", + }, + content: { + flex: 1, + padding: 16, + marginBottom: 15, + }, + infoCard: { + backgroundColor: "#fff", + borderRadius: 12, + padding: 16, + marginBottom: 35, + shadowColor: "#000", + shadowOpacity: 0.05, + shadowRadius: 4, + shadowOffset: { width: 0, height: 2 }, + elevation: 2, + }, + infoRow: { + paddingVertical: 12, + borderBottomWidth: 1, + borderBottomColor: "#f0f0f0", + }, + infoLabel: { + fontSize: 13, + fontWeight: "600", + color: "#666", + marginBottom: 6, + }, + infoValue: { + fontSize: 16, + color: "#000", + fontWeight: "500", + }, + statusBadge: { + paddingHorizontal: 12, + paddingVertical: 6, + borderRadius: 8, + alignSelf: "flex-start", + }, + statusBadgeCompleted: { + backgroundColor: "#e8f5e9", + }, + statusBadgeInProgress: { + backgroundColor: "#fff3e0", + }, + statusBadgeText: { + fontSize: 14, + fontWeight: "600", + }, + statusBadgeTextCompleted: { + color: "#2e7d32", + }, + statusBadgeTextInProgress: { + color: "#f57c00", + }, + headerButtons: { + flexDirection: "row", + alignItems: "center", + gap: 12, + }, + editButton: { + padding: 4, + }, + editIconButton: { + backgroundColor: "#007AFF", + borderRadius: 10, + padding: 10, + justifyContent: "center", + alignItems: "center", + }, + cancelButton: { + paddingHorizontal: 12, + paddingVertical: 6, + }, + cancelButtonText: { + color: "#007AFF", + fontSize: 16, + fontWeight: "600", + }, + saveButton: { + backgroundColor: "#007AFF", + paddingHorizontal: 16, + paddingVertical: 6, + borderRadius: 6, + }, + saveButtonText: { + color: "#fff", + fontSize: 16, + fontWeight: "600", + }, + sectionHeader: { + flexDirection: "row", + justifyContent: "space-between", + alignItems: "center", + marginTop: 16, + marginBottom: 12, + paddingHorizontal: 4, + }, + sectionTitle: { + fontSize: 18, + fontWeight: "700", + color: "#000", + }, + totalCatchText: { + fontSize: 16, + fontWeight: "600", + color: "#007AFF", + }, + fishCard: { + backgroundColor: "#fff", + borderRadius: 12, + padding: 16, + marginBottom: 12, + shadowColor: "#000", + shadowOpacity: 0.05, + shadowRadius: 4, + shadowOffset: { width: 0, height: 2 }, + elevation: 2, + }, + fieldGroup: { + marginBottom: 12, + position: "relative", + }, + rowGroup: { + flexDirection: "row", + marginBottom: 12, + }, + label: { + fontSize: 13, + fontWeight: "600", + color: "#666", + marginBottom: 6, + }, + input: { + borderWidth: 1, + borderColor: "#007AFF", + borderRadius: 8, + paddingHorizontal: 12, + paddingVertical: 10, + fontSize: 15, + color: "#000", + backgroundColor: "#fff", + }, + selectButton: { + borderWidth: 1, + borderColor: "#007AFF", + borderRadius: 8, + paddingHorizontal: 12, + paddingVertical: 10, + flexDirection: "row", + justifyContent: "space-between", + alignItems: "center", + backgroundColor: "#fff", + }, + selectButtonText: { + fontSize: 15, + color: "#000", + }, + optionsList: { + position: "absolute", + top: 46, + left: 0, + right: 0, + borderWidth: 1, + borderColor: "#007AFF", + borderRadius: 8, + marginTop: 4, + backgroundColor: "#fff", + maxHeight: 200, + zIndex: 1000, + elevation: 5, + shadowColor: "#000", + shadowOpacity: 0.15, + shadowRadius: 8, + shadowOffset: { width: 0, height: 4 }, + }, + optionItem: { + paddingHorizontal: 12, + paddingVertical: 12, + borderBottomWidth: 1, + borderBottomColor: "#f0f0f0", + }, + optionText: { + fontSize: 15, + color: "#000", + }, + optionsStatusFishList: { + borderWidth: 1, + borderColor: "#007AFF", + borderRadius: 8, + marginTop: 4, + backgroundColor: "#fff", + maxHeight: 200, + zIndex: 1000, + elevation: 5, + shadowColor: "#000", + shadowOpacity: 0.15, + shadowRadius: 8, + shadowOffset: { width: 0, height: 4 }, + }, +}); + +export default styles;