222 lines
5.4 KiB
TypeScript
222 lines
5.4 KiB
TypeScript
import React, { useEffect, useMemo } from "react";
|
|
import { View, Text, StyleSheet, Platform } from "react-native";
|
|
import { Ionicons } from "@expo/vector-icons";
|
|
import { useI18n } from "@/hooks/use-i18n";
|
|
import { useThemeContext } from "@/hooks/use-theme-context";
|
|
import { useShip } from "@/state/use-ship";
|
|
import { usePort } from "@/state/use-ports";
|
|
import { useGroup } from "@/state/use-group";
|
|
import { filterPortsByProvinceCode } from "@/utils/tripDataConverters";
|
|
|
|
interface BasicInfoSectionProps {
|
|
trip: Model.Trip;
|
|
}
|
|
|
|
/**
|
|
* Displays basic trip information like ship, dates, ports
|
|
*/
|
|
export default function BasicInfoSection({ trip }: BasicInfoSectionProps) {
|
|
const { t } = useI18n();
|
|
const { colors } = useThemeContext();
|
|
|
|
// Get data from zustand stores
|
|
const { ships, getShip } = useShip();
|
|
const { ports, getPorts } = usePort();
|
|
const { groups, getUserGroups } = useGroup();
|
|
|
|
// Fetch data if not available
|
|
useEffect(() => {
|
|
if (!ships) {
|
|
getShip();
|
|
}
|
|
}, [ships, getShip]);
|
|
|
|
useEffect(() => {
|
|
if (!ports) {
|
|
getPorts();
|
|
}
|
|
}, [ports, getPorts]);
|
|
|
|
useEffect(() => {
|
|
if (!groups) {
|
|
getUserGroups();
|
|
}
|
|
}, [groups, getUserGroups]);
|
|
|
|
// Filter ports by province codes from groups
|
|
const filteredPorts = useMemo(() => {
|
|
return filterPortsByProvinceCode(ports, groups);
|
|
}, [ports, groups]);
|
|
|
|
// Get ship name by ship_id
|
|
const shipName = useMemo(() => {
|
|
if (!trip?.ship_id || !ships) return "--";
|
|
const ship = ships.find((s) => s.id === trip.ship_id);
|
|
return ship?.name || "--";
|
|
}, [trip?.ship_id, ships]);
|
|
|
|
// Get ship code (reg_number) by ship_id
|
|
const shipCode = useMemo(() => {
|
|
if (!trip?.ship_id || !ships) return "--";
|
|
const ship = ships.find((s) => s.id === trip.ship_id);
|
|
return ship?.reg_number || "--";
|
|
}, [trip?.ship_id, ships]);
|
|
|
|
// Get port name by ID
|
|
const getPortName = (portId: number): string => {
|
|
const port = filteredPorts.find((p) => p.id === portId);
|
|
return port?.name || "--";
|
|
};
|
|
|
|
const formatDateTime = (dateStr?: string) => {
|
|
if (!dateStr) return "--";
|
|
const date = new Date(dateStr);
|
|
return date.toLocaleString("vi-VN", {
|
|
day: "2-digit",
|
|
month: "2-digit",
|
|
year: "numeric",
|
|
hour: "2-digit",
|
|
minute: "2-digit",
|
|
});
|
|
};
|
|
|
|
const infoItems = [
|
|
{
|
|
icon: "boat" as const,
|
|
label: t("diary.tripDetail.shipName"),
|
|
value: shipName,
|
|
},
|
|
{
|
|
icon: "barcode" as const,
|
|
label: t("diary.tripDetail.shipCode"),
|
|
value: shipCode,
|
|
},
|
|
{
|
|
icon: "play-circle" as const,
|
|
label: t("diary.tripDetail.departureTime"),
|
|
value: formatDateTime(trip.departure_time),
|
|
},
|
|
{
|
|
icon: "stop-circle" as const,
|
|
label: t("diary.tripDetail.arrivalTime"),
|
|
value: formatDateTime(trip.arrival_time),
|
|
},
|
|
{
|
|
icon: "location" as const,
|
|
label: t("diary.tripDetail.departurePort"),
|
|
value: getPortName(trip.departure_port_id),
|
|
},
|
|
{
|
|
icon: "flag" as const,
|
|
label: t("diary.tripDetail.arrivalPort"),
|
|
value: getPortName(trip.arrival_port_id),
|
|
},
|
|
{
|
|
icon: "map" as const,
|
|
label: t("diary.tripDetail.fishingGrounds"),
|
|
value:
|
|
trip.fishing_ground_codes?.length > 0
|
|
? trip.fishing_ground_codes.join(", ")
|
|
: "--",
|
|
},
|
|
];
|
|
|
|
return (
|
|
<View
|
|
style={[
|
|
styles.container,
|
|
{ backgroundColor: colors.card, borderColor: colors.separator },
|
|
]}
|
|
>
|
|
<View style={styles.header}>
|
|
<Ionicons
|
|
name="information-circle-outline"
|
|
size={20}
|
|
color={colors.primary}
|
|
/>
|
|
<Text style={[styles.title, { color: colors.text }]}>
|
|
{t("diary.tripDetail.basicInfo")}
|
|
</Text>
|
|
</View>
|
|
<View style={styles.content}>
|
|
{infoItems.map((item, index) => (
|
|
<View key={index} style={styles.infoItem}>
|
|
<View style={styles.infoLabel}>
|
|
<Ionicons
|
|
name={item.icon}
|
|
size={16}
|
|
color={colors.textSecondary}
|
|
/>
|
|
<Text
|
|
style={[styles.infoLabelText, { color: colors.textSecondary }]}
|
|
>
|
|
{item.label}
|
|
</Text>
|
|
</View>
|
|
<Text style={[styles.infoValue, { color: colors.text }]}>
|
|
{item.value}
|
|
</Text>
|
|
</View>
|
|
))}
|
|
</View>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
borderRadius: 12,
|
|
borderWidth: 1,
|
|
marginBottom: 16,
|
|
overflow: "hidden",
|
|
},
|
|
header: {
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
gap: 10,
|
|
paddingHorizontal: 16,
|
|
paddingVertical: 14,
|
|
},
|
|
title: {
|
|
fontSize: 16,
|
|
fontWeight: "600",
|
|
fontFamily: Platform.select({
|
|
ios: "System",
|
|
android: "Roboto",
|
|
default: "System",
|
|
}),
|
|
},
|
|
content: {
|
|
paddingHorizontal: 16,
|
|
paddingBottom: 16,
|
|
gap: 12,
|
|
},
|
|
infoItem: {
|
|
flexDirection: "row",
|
|
justifyContent: "space-between",
|
|
alignItems: "center",
|
|
},
|
|
infoLabel: {
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
gap: 8,
|
|
},
|
|
infoLabelText: {
|
|
fontSize: 13,
|
|
fontFamily: Platform.select({
|
|
ios: "System",
|
|
android: "Roboto",
|
|
default: "System",
|
|
}),
|
|
},
|
|
infoValue: {
|
|
fontSize: 13,
|
|
fontWeight: "500",
|
|
fontFamily: Platform.select({
|
|
ios: "System",
|
|
android: "Roboto",
|
|
default: "System",
|
|
}),
|
|
},
|
|
});
|