diff --git a/app/(tabs)/tripInfo.tsx b/app/(tabs)/tripInfo.tsx index eb74ff1..934a8b3 100644 --- a/app/(tabs)/tripInfo.tsx +++ b/app/(tabs)/tripInfo.tsx @@ -15,6 +15,7 @@ export default function TripInfoScreen() { useEffect(() => { getTrip(); }, []); + return ( diff --git a/components/tripInfo/CrewListTable.tsx b/components/tripInfo/CrewListTable.tsx index 09f769c..1fd6fb7 100644 --- a/components/tripInfo/CrewListTable.tsx +++ b/components/tripInfo/CrewListTable.tsx @@ -1,86 +1,162 @@ import { IconSymbol } from "@/components/ui/icon-symbol"; -import React, { useRef, useState } from "react"; +import { useTrip } from "@/state/use-trip"; +import React, { useEffect, useRef, useState } from "react"; import { Animated, Text, TouchableOpacity, View } from "react-native"; import CrewDetailModal from "./modal/CrewDetailModal"; import styles from "./style/CrewListTable.styles"; -// --------------------------- -// 🧩 Interface -// --------------------------- -interface CrewMember { - id: string; - maDinhDanh: string; - ten: string; - chucVu: string; - ngaySinh?: string; - cccd?: string; - soDienThoai?: string; - diaChi?: string; - ngayVaoLam?: string; - trinhDoChuyenMon?: string; - bangCap?: string; - tinhTrang?: string; -} - -// --------------------------- -// ⚓ Dữ liệu mẫu -// --------------------------- -const data: CrewMember[] = [ +const mockCrews: Model.TripCrews[] = [ { - id: "10", - maDinhDanh: "ChuTau", - ten: "Nguyễn Nhật Minh", - chucVu: "Chủ tàu", - ngaySinh: "08/06/2006", - cccd: "079085012345", - soDienThoai: "0912345678", - diaChi: "Hà Nội", - ngayVaoLam: "", - trinhDoChuyenMon: "Thuyền trưởng hạng I", - bangCap: "Bằng thuyền trưởng xa bờ", - tinhTrang: "Đang làm việc", + TripID: "f9884294-a7f2-46dc-aaf2-032da08a1ab6", + PersonalID: "480863197307", + role: "crew", + joined_at: new Date("2025-11-06T08:13:33.230053Z"), + left_at: null, + note: null, + Person: { + personal_id: "480863197307", + name: "Huỳnh Tấn Trang", + phone: "0838944284", + email: "huynhtantrang@crew.sgw.vn", + birth_date: new Date("2025-01-11T00:00:00Z"), + note: "", + address: "49915 Poplar Avenue", + created_at: new Date("0001-01-01T00:00:00Z"), + updated_at: new Date("0001-01-01T00:00:00Z"), + }, }, { - id: "1", - maDinhDanh: "TV001", - ten: "Nguyễn Văn A", - chucVu: "Thuyền trưởng", - ngaySinh: "20/05/1988", - cccd: "079088011111", - soDienThoai: "0901234567", - diaChi: "456 Đường Cảng, Phường Thanh Khê, Đà Nẵng", - ngayVaoLam: "15/06/2015", - trinhDoChuyenMon: "Thuyền trưởng hạng II", - bangCap: "Bằng thuyền trưởng ven bờ", - tinhTrang: "Đang làm việc", + TripID: "f9884294-a7f2-46dc-aaf2-032da08a1ab6", + PersonalID: "714834545296", + role: "crew", + joined_at: new Date("2025-11-06T08:13:33.301376Z"), + left_at: null, + note: null, + Person: { + personal_id: "714834545296", + name: "Trương Văn Nam", + phone: "0773396753", + email: "truongvannam@crew.sgw.vn", + birth_date: new Date("2025-07-24T00:00:00Z"), + note: "", + address: "3287 Carlotta Underpass", + created_at: new Date("0001-01-01T00:00:00Z"), + updated_at: new Date("0001-01-01T00:00:00Z"), + }, }, { - id: "2", - maDinhDanh: "TV002", - ten: "Trần Văn B", - chucVu: "Máy trưởng", - ngaySinh: "10/08/1990", - cccd: "079090022222", - soDienThoai: "0987654321", - diaChi: "789 Đường Nguyễn Văn Linh, Quận Sơn Trà, Đà Nẵng", - ngayVaoLam: "20/03/2016", - trinhDoChuyenMon: "Máy trưởng hạng III", - bangCap: "Bằng máy trưởng ven bờ", - tinhTrang: "Đang làm việc", + TripID: "f9884294-a7f2-46dc-aaf2-032da08a1ab6", + PersonalID: "049299828990", + role: "crew", + joined_at: new Date("2025-11-06T08:13:33.373037Z"), + left_at: null, + note: null, + Person: { + personal_id: "049299828990", + name: "Đặng Anh Minh", + phone: "0827640820", + email: "danganhminh@crew.sgw.vn", + birth_date: new Date("2024-10-30T00:00:00Z"), + note: "", + address: "68909 Gerda Burgs", + created_at: new Date("0001-01-01T00:00:00Z"), + updated_at: new Date("0001-01-01T00:00:00Z"), + }, }, { - id: "3", - maDinhDanh: "TV003", - ten: "Lê Văn C", - chucVu: "Thủy thủ", - ngaySinh: "25/12/1995", - cccd: "079095033333", - soDienThoai: "0976543210", - diaChi: "321 Đường Hoàng Sa, Quận Ngũ Hành Sơn, Đà Nẵng", - ngayVaoLam: "10/07/2018", - trinhDoChuyenMon: "Thủy thủ hạng I", - bangCap: "Chứng chỉ thủy thủ", - tinhTrang: "Đang làm việc", + TripID: "f9884294-a7f2-46dc-aaf2-032da08a1ab6", + PersonalID: "851494873747", + role: "captain", + joined_at: new Date("2025-11-06T08:13:33.442774Z"), + left_at: null, + note: null, + Person: { + personal_id: "851494873747", + name: "Tô Thị Linh", + phone: "0337906041", + email: "tothilinh@crew.sgw.vn", + birth_date: new Date("2025-06-18T00:00:00Z"), + note: "", + address: "6676 Kulas Groves", + created_at: new Date("0001-01-01T00:00:00Z"), + updated_at: new Date("0001-01-01T00:00:00Z"), + }, + }, + { + TripID: "f9884294-a7f2-46dc-aaf2-032da08a1ab6", + PersonalID: "384839614682", + role: "crew", + joined_at: new Date("2025-11-06T08:13:33.515532Z"), + left_at: null, + note: null, + Person: { + personal_id: "384839614682", + name: "Lê Thanh Hoa", + phone: "0937613034", + email: "lethanhhoa@crew.sgw.vn", + birth_date: new Date("2025-07-17T00:00:00Z"), + note: "", + address: "244 Cicero Estate", + created_at: new Date("0001-01-01T00:00:00Z"), + updated_at: new Date("0001-01-01T00:00:00Z"), + }, + }, + { + TripID: "f9884294-a7f2-46dc-aaf2-032da08a1ab6", + PersonalID: "702319275290", + role: "crew", + joined_at: new Date("2025-11-06T08:13:33.588038Z"), + left_at: null, + note: null, + Person: { + personal_id: "702319275290", + name: "Nguyễn Phước Hải", + phone: "0347859214", + email: "nguyenphuochai@crew.sgw.vn", + birth_date: new Date("2025-08-13T00:00:00Z"), + note: "", + address: "6874 Devon Key", + created_at: new Date("0001-01-01T00:00:00Z"), + updated_at: new Date("0001-01-01T00:00:00Z"), + }, + }, + { + TripID: "f9884294-a7f2-46dc-aaf2-032da08a1ab6", + PersonalID: "943534816439", + role: "crew", + joined_at: new Date("2025-11-06T08:13:33.668984Z"), + left_at: null, + note: null, + Person: { + personal_id: "943534816439", + name: "Lý Hữu Hà", + phone: "0768548881", + email: "lyhuuha@crew.sgw.vn", + birth_date: new Date("2025-08-18T00:00:00Z"), + note: "", + address: "655 Middle Street", + created_at: new Date("0001-01-01T00:00:00Z"), + updated_at: new Date("0001-01-01T00:00:00Z"), + }, + }, + { + TripID: "f9884294-a7f2-46dc-aaf2-032da08a1ab6", + PersonalID: "096528446981", + role: "crew", + joined_at: new Date("2025-11-06T08:13:33.74379Z"), + left_at: null, + note: null, + Person: { + personal_id: "096528446981", + name: "Trần Xuân Thi", + phone: "0963449523", + email: "tranxuanthi@crew.sgw.vn", + birth_date: new Date("2024-09-21T00:00:00Z"), + note: "", + address: "59344 Burley Isle", + created_at: new Date("0001-01-01T00:00:00Z"), + updated_at: new Date("0001-01-01T00:00:00Z"), + }, }, ]; @@ -89,9 +165,22 @@ const CrewListTable: React.FC = () => { const [contentHeight, setContentHeight] = useState(0); const animatedHeight = useRef(new Animated.Value(0)).current; const [modalVisible, setModalVisible] = useState(false); - const [selectedCrew, setSelectedCrew] = useState(null); + const [selectedCrew, setSelectedCrew] = useState( + null + ); + + const { trip } = useTrip(); + + const data: Model.TripCrews[] = trip?.crews ?? mockCrews; + const tongThanhVien = data.length; + // Reset animated height khi dữ liệu thay đổi + useEffect(() => { + setContentHeight(0); // Reset để tính lại chiều cao + setCollapsed(true); // Reset về trạng thái gập lại + }, [data]); + const handleToggle = () => { const toValue = collapsed ? contentHeight : 0; Animated.timing(animatedHeight, { @@ -103,7 +192,7 @@ const CrewListTable: React.FC = () => { }; const handleCrewPress = (crewId: string) => { - const crew = data.find((item) => item.id === crewId); + const crew = data.find((item) => item.Person.personal_id === crewId); if (crew) { setSelectedCrew(crew); setModalVisible(true); @@ -146,9 +235,6 @@ const CrewListTable: React.FC = () => { > {/* Header */} - - Mã định danh - Tên @@ -159,25 +245,23 @@ const CrewListTable: React.FC = () => { {/* Body */} {data.map((item) => ( - - {item.maDinhDanh} + handleCrewPress(item.id)} + onPress={() => handleCrewPress(item.Person.personal_id)} > - {item.ten} + + {item.Person.name} + - {item.chucVu} + {item.role} ))} {/* Footer */} - - Tổng cộng - + Tổng cộng {tongThanhVien} - @@ -185,9 +269,6 @@ const CrewListTable: React.FC = () => { {/* Header */} - - Mã định danh - Tên @@ -198,25 +279,23 @@ const CrewListTable: React.FC = () => { {/* Body */} {data.map((item) => ( - - {item.maDinhDanh} + handleCrewPress(item.id)} + onPress={() => handleCrewPress(item.Person.personal_id)} > - {item.ten} + + {item.Person.name} + - {item.chucVu} + {item.role} ))} {/* Footer */} - - Tổng cộng - + Tổng cộng {tongThanhVien} - diff --git a/components/tripInfo/FishingToolsList.tsx b/components/tripInfo/FishingToolsList.tsx index 5e7ec30..f108fc0 100644 --- a/components/tripInfo/FishingToolsList.tsx +++ b/components/tripInfo/FishingToolsList.tsx @@ -1,31 +1,23 @@ import { IconSymbol } from "@/components/ui/icon-symbol"; -import React, { useRef, useState } from "react"; +import { useTrip } from "@/state/use-trip"; +import React, { useEffect, useRef, useState } from "react"; import { Animated, Text, TouchableOpacity, 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 = () => { 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 { trip } = useTrip(); + const data: Model.FishingGear[] = trip?.fishing_gears ?? []; + const tongSoLuong = data.reduce((sum, item) => sum + Number(item.number), 0); + + // Reset animated height khi dữ liệu thay đổi + useEffect(() => { + setContentHeight(0); // Reset để tính lại chiều cao + setCollapsed(true); // Reset về trạng thái gập lại + }, [data]); const handleToggle = () => { const toValue = collapsed ? contentHeight : 0; @@ -73,10 +65,10 @@ const FishingToolsTable: React.FC = () => { {/* Body */} - {data.map((item) => ( - - {item.ten} - {item.soLuong} + {data.map((item, index) => ( + + {item.name} + {item.number} ))} @@ -102,10 +94,10 @@ const FishingToolsTable: React.FC = () => { {/* Body */} - {data.map((item) => ( - - {item.ten} - {item.soLuong} + {data.map((item, index) => ( + + {item.name} + {item.number} ))} diff --git a/components/tripInfo/TripCostTable.tsx b/components/tripInfo/TripCostTable.tsx index 2717582..c05eb5f 100644 --- a/components/tripInfo/TripCostTable.tsx +++ b/components/tripInfo/TripCostTable.tsx @@ -1,60 +1,10 @@ import { IconSymbol } from "@/components/ui/icon-symbol"; import { useTrip } from "@/state/use-trip"; -import React, { useRef, useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; import { Animated, Text, TouchableOpacity, View } from "react-native"; import TripCostDetailModal from "./modal/TripCostDetailModal"; import styles from "./style/TripCostTable.styles"; -// --------------------------- -// 🧩 Interface -// --------------------------- -interface CostItem { - id: string; - loai: string; - soLuong: number; - donVi: string; - chiPhi: number; - tongChiPhi: number; -} - -// --------------------------- -// 📊 Dữ liệu mẫu -// --------------------------- -const data: CostItem[] = [ - { - id: "1", - loai: "Nhiên liệu", - soLuong: 1000, - donVi: "liters", - chiPhi: 20000, - tongChiPhi: 20000000, - }, - { - id: "2", - loai: "Lương thực", - soLuong: 500, - donVi: "kg", - chiPhi: 30000, - tongChiPhi: 15000000, - }, - { - id: "3", - loai: "Lương thuyền viên", - soLuong: 10, - donVi: "people", - chiPhi: 5000000, - tongChiPhi: 50000000, - }, - { - id: "4", - loai: "Muối đá", - soLuong: 100, - donVi: "kg", - chiPhi: 20000, - tongChiPhi: 2000000, - }, -]; - // --------------------------- // 💰 Component chính // --------------------------- @@ -64,8 +14,17 @@ const TripCostTable: React.FC = () => { const [contentHeight, setContentHeight] = useState(0); const [modalVisible, setModalVisible] = useState(false); const animatedHeight = useRef(new Animated.Value(0)).current; - const tongCong = data.reduce((sum, item) => sum + item.tongChiPhi, 0); - const { trip, getTrip } = useTrip(); + + const { trip } = useTrip(); + + const data: Model.TripCost[] = trip?.trip_cost ?? []; + const tongCong = data.reduce((sum, item) => sum + item.total_cost, 0); + + // Reset animated height khi dữ liệu thay đổi + useEffect(() => { + setContentHeight(0); // Reset để tính lại chiều cao + setCollapsed(true); // Reset về trạng thái gập lại + }, [data]); const handleToggle = () => { const toValue = collapsed ? contentHeight : 0; @@ -97,8 +56,6 @@ const TripCostTable: React.FC = () => { // marginBottom: 12, }} > - {/* {trip && } */} - Chi phí chuyến đi {collapsed && ( { {/* Body */} - {data.map((item) => ( - - {item.loai} + {data.map((item, index) => ( + + {item.type} - {item.tongChiPhi.toLocaleString()} + {item.total_cost.toLocaleString()} ))} @@ -156,12 +113,14 @@ const TripCostTable: React.FC = () => { {/* View Detail Button */} - - Xem chi tiết - + {data.length > 0 && ( + + Xem chi tiết + + )} @@ -174,11 +133,11 @@ const TripCostTable: React.FC = () => { {/* Body */} - {data.map((item) => ( - - {item.loai} + {data.map((item, index) => ( + + {item.type} - {item.tongChiPhi.toLocaleString()} + {item.total_cost.toLocaleString()} ))} @@ -194,12 +153,14 @@ const TripCostTable: React.FC = () => { {/* View Detail Button */} - - Xem chi tiết - + {data.length > 0 && ( + + Xem chi tiết + + )} {/* Modal */} diff --git a/components/tripInfo/modal/CrewDetailModal.tsx b/components/tripInfo/modal/CrewDetailModal.tsx index 13b776b..3d50b34 100644 --- a/components/tripInfo/modal/CrewDetailModal.tsx +++ b/components/tripInfo/modal/CrewDetailModal.tsx @@ -6,25 +6,11 @@ import styles from "./style/CrewDetailModal.styles"; // --------------------------- // 🧩 Interface // --------------------------- -interface CrewMember { - id: string; - maDinhDanh: string; - ten: string; - chucVu: string; - ngaySinh?: string; - cccd?: string; - soDienThoai?: string; - diaChi?: string; - ngayVaoLam?: string; - trinhDoChuyenMon?: string; - bangCap?: string; - tinhTrang?: string; -} interface CrewDetailModalProps { visible: boolean; onClose: () => void; - crewData: CrewMember | null; + crewData: Model.TripCrews | null; } // --------------------------- @@ -38,20 +24,28 @@ const CrewDetailModal: React.FC = ({ if (!crewData) return null; const infoItems = [ - { label: "Mã định danh", value: crewData.maDinhDanh }, - { label: "Họ và tên", value: crewData.ten }, - { label: "Chức vụ", value: crewData.chucVu }, - { label: "Ngày sinh", value: crewData.ngaySinh || "Chưa cập nhật" }, - { label: "CCCD/CMND", value: crewData.cccd || "Chưa cập nhật" }, - { label: "Số điện thoại", value: crewData.soDienThoai || "Chưa cập nhật" }, - { label: "Địa chỉ", value: crewData.diaChi || "Chưa cập nhật" }, - { label: "Ngày vào làm", value: crewData.ngayVaoLam || "Chưa cập nhật" }, + { label: "Mã định danh", value: crewData.Person.personal_id }, + { label: "Họ và tên", value: crewData.Person.name }, + { label: "Chức vụ", value: crewData.role }, { - label: "Trình độ chuyên môn", - value: crewData.trinhDoChuyenMon || "Chưa cập nhật", + label: "Ngày sinh", + value: crewData.Person.birth_date + ? new Date(crewData.Person.birth_date).toLocaleDateString() + : "Chưa cập nhật", + }, + { label: "Số điện thoại", value: crewData.Person.phone || "Chưa cập nhật" }, + { label: "Địa chỉ", value: crewData.Person.address || "Chưa cập nhật" }, + { + label: "Ngày vào làm", + value: crewData.joined_at + ? new Date(crewData.joined_at).toLocaleDateString() + : "Chưa cập nhật", + }, + { label: "Ghi chú", value: crewData.note || "Chưa cập nhật" }, + { + label: "Tình trạng", + value: crewData.left_at ? "Đã nghỉ" : "Đang làm việc", }, - { label: "Bằng cấp", value: crewData.bangCap || "Chưa cập nhật" }, - { label: "Tình trạng", value: crewData.tinhTrang || "Đang làm việc" }, ]; return ( diff --git a/components/tripInfo/modal/TripCostDetailModal.tsx b/components/tripInfo/modal/TripCostDetailModal.tsx index e6f9b1d..1fd5431 100644 --- a/components/tripInfo/modal/TripCostDetailModal.tsx +++ b/components/tripInfo/modal/TripCostDetailModal.tsx @@ -1,5 +1,5 @@ import { IconSymbol } from "@/components/ui/icon-symbol"; -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import { KeyboardAvoidingView, Modal, @@ -15,19 +15,10 @@ import styles from "./style/TripCostDetailModal.styles"; // --------------------------- // 🧩 Interface // --------------------------- -interface CostItem { - id: string; - loai: string; - soLuong: number; - donVi: string; - chiPhi: number; - tongChiPhi: number; -} - interface TripCostDetailModalProps { visible: boolean; onClose: () => void; - data: CostItem[]; + data: Model.TripCost[]; } // --------------------------- @@ -39,9 +30,14 @@ const TripCostDetailModal: React.FC = ({ data, }) => { const [isEditing, setIsEditing] = useState(false); - const [editableData, setEditableData] = useState(data); + const [editableData, setEditableData] = useState(data); - const tongCong = editableData.reduce((sum, item) => sum + item.tongChiPhi, 0); + // Cập nhật editableData khi props data thay đổi (API fetch xong) + useEffect(() => { + setEditableData(data); + }, [data]); + + const tongCong = editableData.reduce((sum, item) => sum + item.total_cost, 0); const handleEdit = () => { setIsEditing(!isEditing); @@ -50,7 +46,7 @@ const TripCostDetailModal: React.FC = ({ const handleSave = () => { setIsEditing(false); // TODO: Save data to backend - // console.log("Saved data:", editableData); + console.log("Saved data:", editableData); }; const handleCancel = () => { @@ -58,16 +54,23 @@ const TripCostDetailModal: React.FC = ({ setEditableData(data); // Reset to original data }; - const updateItem = (id: string, field: keyof CostItem, value: string) => { + const updateItem = ( + index: number, + field: keyof Model.TripCost, + value: string + ) => { setEditableData((prev) => - prev.map((item) => { - if (item.id === id) { - const numValue = - field === "loai" || field === "donVi" ? value : Number(value) || 0; - const updated = { ...item, [field]: numValue }; - // Recalculate tongChiPhi - if (field === "soLuong" || field === "chiPhi") { - updated.tongChiPhi = updated.soLuong * updated.chiPhi; + prev.map((item, idx) => { + if (idx === index) { + const updated = { ...item, [field]: value }; + // Recalculate total_cost + if (field === "amount" || field === "cost_per_unit") { + const amount = + Number(field === "amount" ? value : item.amount) || 0; + const costPerUnit = + Number(field === "cost_per_unit" ? value : item.cost_per_unit) || + 0; + updated.total_cost = amount * costPerUnit; } return updated; } @@ -133,15 +136,15 @@ const TripCostDetailModal: React.FC = ({ {/* Content */} - {editableData.map((item) => ( - + {editableData.map((item, index) => ( + {/* Loại */} Loại chi phí updateItem(item.id, "loai", value)} + value={item.type} + onChangeText={(value) => updateItem(index, "type", value)} editable={isEditing} placeholder="Nhập loại chi phí" /> @@ -155,9 +158,9 @@ const TripCostDetailModal: React.FC = ({ Số lượng - updateItem(item.id, "soLuong", value) + updateItem(index, "amount", value) } editable={isEditing} keyboardType="numeric" @@ -168,10 +171,8 @@ const TripCostDetailModal: React.FC = ({ Đơn vị - updateItem(item.id, "donVi", value) - } + value={item.unit} + onChangeText={(value) => updateItem(index, "unit", value)} editable={isEditing} placeholder="kg, lít..." /> @@ -183,9 +184,9 @@ const TripCostDetailModal: React.FC = ({ Chi phí/đơn vị (VNĐ) - updateItem(item.id, "chiPhi", value) + updateItem(index, "cost_per_unit", value) } editable={isEditing} keyboardType="numeric" @@ -198,7 +199,7 @@ const TripCostDetailModal: React.FC = ({ Tổng chi phí - {item.tongChiPhi.toLocaleString()} VNĐ + {item.total_cost.toLocaleString()} VNĐ diff --git a/components/tripInfo/style/CrewListTable.styles.ts b/components/tripInfo/style/CrewListTable.styles.ts index 4288a48..725fbc0 100644 --- a/components/tripInfo/style/CrewListTable.styles.ts +++ b/components/tripInfo/style/CrewListTable.styles.ts @@ -48,13 +48,10 @@ export default StyleSheet.create({ textAlign: "center", }, cellWrapper: { - flex: 1.5, + flex: 1, justifyContent: "center", alignItems: "center", }, - left: { - textAlign: "center", - }, right: { textAlign: "center", }, diff --git a/controller/typings.d.ts b/controller/typings.d.ts index 8551605..e92b6c0 100644 --- a/controller/typings.d.ts +++ b/controller/typings.d.ts @@ -121,19 +121,32 @@ declare namespace Model { } // Thuyền viên interface TripCrews { + TripID: string; + PersonalID: string; role: string; joined_at: Date; left_at: Date | null; note: string | null; Person: TripCrewPerson; } + interface TripCrewPerson { + personal_id: string; + name: string; + phone: string; + email: string; + birth_date: Date; // ISO string (có thể chuyển sang Date nếu parse trước) + note: string; + address: string; + created_at: Date; + updated_at: Date; + } // Chi phí chuyến đi interface TripCost { type: string; unit: string; - amount: string; - total_cost: string; - cost_per_unit: string; + amount: number; + total_cost: number; + cost_per_unit: number; } // Thông tin mẻ lưới interface FishingLog {