Files
sgw-owner-app/components/diary/TripDetailSections/FishingLogsSection.tsx

357 lines
10 KiB
TypeScript

import React from "react";
import { View, Text, StyleSheet, Platform } from "react-native";
import { Ionicons } from "@expo/vector-icons";
import { useThemeContext } from "@/hooks/use-theme-context";
import { useI18n } from "@/hooks/use-i18n";
import SectionCard from "./SectionCard";
interface FishingLogsSectionProps {
fishingLogs?: Model.FishingLog[] | null;
}
export default function FishingLogsSection({ fishingLogs = [] }: FishingLogsSectionProps) {
const { t } = useI18n();
const { colors } = useThemeContext();
const logs = fishingLogs || [];
const formatDateTime = (date?: Date) => {
if (!date) return "--";
const d = new Date(date);
return d.toLocaleString("vi-VN", {
day: "2-digit",
month: "2-digit",
year: "numeric",
hour: "2-digit",
minute: "2-digit",
});
};
const formatCoord = (lat?: number, lon?: number) => {
if (lat === undefined || lon === undefined) return "--";
return `${lat.toFixed(4)}°N, ${lon.toFixed(4)}°E`;
};
const getStatusLabel = (status?: number) => {
switch (status) {
case 0:
return { label: t("diary.tripDetail.logStatusPending"), color: "#FEF3C7", textColor: "#92400E" };
case 1:
return { label: t("diary.tripDetail.logStatusActive"), color: "#DBEAFE", textColor: "#1E40AF" };
case 2:
return { label: t("diary.tripDetail.logStatusCompleted"), color: "#D1FAE5", textColor: "#065F46" };
default:
return { label: t("diary.tripDetail.logStatusUnknown"), color: "#F3F4F6", textColor: "#4B5563" };
}
};
return (
<SectionCard
title={t("diary.tripDetail.fishingLogs")}
icon="boat-outline"
count={logs.length}
collapsible
defaultExpanded={true}
>
{logs.length === 0 ? (
<View style={styles.emptyContainer}>
<Ionicons name="fish-outline" size={40} color={colors.textSecondary} />
<Text style={[styles.emptyText, { color: colors.textSecondary }]}>
{t("diary.tripDetail.noFishingLogs")}
</Text>
</View>
) : (
<View style={styles.list}>
{logs.map((log, index) => {
const status = getStatusLabel(log.status);
const catchCount = log.info?.length || 0;
return (
<View
key={log.fishing_log_id || index}
style={[styles.logItem, { borderColor: colors.separator }]}
>
{/* Header */}
<View style={styles.logHeader}>
<View style={styles.logIndex}>
<Text style={[styles.logIndexText, { color: colors.primary }]}>
#{index + 1}
</Text>
</View>
<View style={[styles.statusBadge, { backgroundColor: status.color }]}>
<Text style={[styles.statusText, { color: status.textColor }]}>
{status.label}
</Text>
</View>
</View>
{/* Time Info */}
<View style={styles.timeRow}>
<View style={styles.timeItem}>
<Ionicons name="play-circle-outline" size={16} color={colors.success || "#22C55E"} />
<Text style={[styles.timeLabel, { color: colors.textSecondary }]}>
{t("diary.tripDetail.startTime")}:
</Text>
<Text style={[styles.timeValue, { color: colors.text }]}>
{formatDateTime(log.start_at)}
</Text>
</View>
<View style={styles.timeItem}>
<Ionicons name="stop-circle-outline" size={16} color={colors.error || "#EF4444"} />
<Text style={[styles.timeLabel, { color: colors.textSecondary }]}>
{t("diary.tripDetail.endTime")}:
</Text>
<Text style={[styles.timeValue, { color: colors.text }]}>
{formatDateTime(log.end_at)}
</Text>
</View>
</View>
{/* Location Info */}
<View style={[styles.locationContainer, { backgroundColor: colors.backgroundSecondary }]}>
<View style={styles.locationItem}>
<Ionicons name="location" size={14} color={colors.success || "#22C55E"} />
<Text style={[styles.locationLabel, { color: colors.textSecondary }]}>
{t("diary.tripDetail.startLocation")}:
</Text>
<Text style={[styles.locationValue, { color: colors.text }]}>
{formatCoord(log.start_lat, log.start_lon)}
</Text>
</View>
<View style={styles.locationItem}>
<Ionicons name="location" size={14} color={colors.error || "#EF4444"} />
<Text style={[styles.locationLabel, { color: colors.textSecondary }]}>
{t("diary.tripDetail.haulLocation")}:
</Text>
<Text style={[styles.locationValue, { color: colors.text }]}>
{formatCoord(log.haul_lat, log.haul_lon)}
</Text>
</View>
</View>
{/* Catch Info */}
{catchCount > 0 && (
<View style={styles.catchContainer}>
<View style={styles.catchHeader}>
<Ionicons name="fish" size={16} color={colors.primary} />
<Text style={[styles.catchLabel, { color: colors.text }]}>
{t("diary.tripDetail.catchInfo")} ({catchCount} {t("diary.tripDetail.species")})
</Text>
</View>
<View style={styles.catchList}>
{log.info?.slice(0, 3).map((fish, fishIndex) => (
<View key={fishIndex} style={styles.catchItem}>
<Text style={[styles.fishName, { color: colors.text }]}>
{fish.fish_name || t("diary.tripDetail.unknownFish")}
</Text>
<Text style={[styles.fishAmount, { color: colors.textSecondary }]}>
{fish.catch_number} {fish.catch_unit}
</Text>
</View>
))}
{catchCount > 3 && (
<Text style={[styles.moreText, { color: colors.primary }]}>
+{catchCount - 3} {t("diary.tripDetail.more")}
</Text>
)}
</View>
</View>
)}
{/* Weather */}
{log.weather_description && (
<View style={styles.weatherRow}>
<Ionicons name="cloudy-outline" size={14} color={colors.textSecondary} />
<Text style={[styles.weatherText, { color: colors.textSecondary }]}>
{log.weather_description}
</Text>
</View>
)}
</View>
);
})}
</View>
)}
</SectionCard>
);
}
const styles = StyleSheet.create({
emptyContainer: {
alignItems: "center",
justifyContent: "center",
paddingVertical: 24,
gap: 8,
},
emptyText: {
fontSize: 14,
fontFamily: Platform.select({
ios: "System",
android: "Roboto",
default: "System",
}),
},
list: {
gap: 16,
},
logItem: {
borderWidth: 1,
borderRadius: 10,
padding: 12,
},
logHeader: {
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
marginBottom: 10,
},
logIndex: {
paddingHorizontal: 10,
paddingVertical: 4,
borderRadius: 4,
backgroundColor: "rgba(59, 130, 246, 0.1)",
},
logIndexText: {
fontSize: 14,
fontWeight: "700",
fontFamily: Platform.select({
ios: "System",
android: "Roboto",
default: "System",
}),
},
statusBadge: {
paddingHorizontal: 10,
paddingVertical: 4,
borderRadius: 4,
},
statusText: {
fontSize: 12,
fontWeight: "600",
fontFamily: Platform.select({
ios: "System",
android: "Roboto",
default: "System",
}),
},
timeRow: {
gap: 6,
marginBottom: 10,
},
timeItem: {
flexDirection: "row",
alignItems: "center",
gap: 6,
},
timeLabel: {
fontSize: 12,
fontFamily: Platform.select({
ios: "System",
android: "Roboto",
default: "System",
}),
},
timeValue: {
fontSize: 12,
fontWeight: "500",
fontFamily: Platform.select({
ios: "System",
android: "Roboto",
default: "System",
}),
},
locationContainer: {
padding: 10,
borderRadius: 6,
gap: 6,
marginBottom: 10,
},
locationItem: {
flexDirection: "row",
alignItems: "center",
gap: 6,
},
locationLabel: {
fontSize: 11,
fontFamily: Platform.select({
ios: "System",
android: "Roboto",
default: "System",
}),
},
locationValue: {
fontSize: 11,
fontWeight: "500",
fontFamily: Platform.select({
ios: "System",
android: "Roboto",
default: "System",
}),
},
catchContainer: {
marginBottom: 10,
},
catchHeader: {
flexDirection: "row",
alignItems: "center",
gap: 6,
marginBottom: 6,
},
catchLabel: {
fontSize: 13,
fontWeight: "600",
fontFamily: Platform.select({
ios: "System",
android: "Roboto",
default: "System",
}),
},
catchList: {
gap: 4,
paddingLeft: 22,
},
catchItem: {
flexDirection: "row",
justifyContent: "space-between",
},
fishName: {
fontSize: 12,
fontFamily: Platform.select({
ios: "System",
android: "Roboto",
default: "System",
}),
},
fishAmount: {
fontSize: 12,
fontFamily: Platform.select({
ios: "System",
android: "Roboto",
default: "System",
}),
},
moreText: {
fontSize: 12,
fontWeight: "500",
fontFamily: Platform.select({
ios: "System",
android: "Roboto",
default: "System",
}),
},
weatherRow: {
flexDirection: "row",
alignItems: "center",
gap: 6,
},
weatherText: {
fontSize: 12,
fontStyle: "italic",
fontFamily: Platform.select({
ios: "System",
android: "Roboto",
default: "System",
}),
},
});