144 lines
4.1 KiB
TypeScript
144 lines
4.1 KiB
TypeScript
import { AlarmData } from "@/app/(tabs)";
|
|
import { ThemedText } from "@/components/themed-text";
|
|
import { formatTimestamp } from "@/services/time_service";
|
|
import { Ionicons } from "@expo/vector-icons";
|
|
import { useCallback } from "react";
|
|
import { FlatList, TouchableOpacity, View } from "react-native";
|
|
|
|
// ============ Types ============
|
|
type AlarmType = "approaching" | "entered" | "fishing";
|
|
|
|
interface AlarmCardProps {
|
|
alarm: AlarmData;
|
|
onPress?: () => void;
|
|
}
|
|
|
|
// ============ Config ============
|
|
const ALARM_CONFIG: Record<
|
|
AlarmType,
|
|
{
|
|
icon: keyof typeof Ionicons.glyphMap;
|
|
label: string;
|
|
bgColor: string;
|
|
borderColor: string;
|
|
iconBgColor: string;
|
|
iconColor: string;
|
|
labelColor: string;
|
|
}
|
|
> = {
|
|
entered: {
|
|
icon: "warning",
|
|
label: "Xâm nhập",
|
|
bgColor: "bg-red-50",
|
|
borderColor: "border-red-200",
|
|
iconBgColor: "bg-red-100",
|
|
iconColor: "#DC2626",
|
|
labelColor: "text-red-600",
|
|
},
|
|
approaching: {
|
|
icon: "alert-circle",
|
|
label: "Tiếp cận",
|
|
bgColor: "bg-amber-50",
|
|
borderColor: "border-amber-200",
|
|
iconBgColor: "bg-amber-100",
|
|
iconColor: "#D97706",
|
|
labelColor: "text-amber-600",
|
|
},
|
|
fishing: {
|
|
icon: "fish",
|
|
label: "Đánh bắt",
|
|
bgColor: "bg-orange-50",
|
|
borderColor: "border-orange-200",
|
|
iconBgColor: "bg-orange-100",
|
|
iconColor: "#EA580C",
|
|
labelColor: "text-orange-600",
|
|
},
|
|
};
|
|
|
|
// ============ AlarmCard Component ============
|
|
const AlarmCard = ({ alarm, onPress }: AlarmCardProps) => {
|
|
const config = ALARM_CONFIG[alarm.type];
|
|
|
|
return (
|
|
<TouchableOpacity
|
|
onPress={onPress}
|
|
activeOpacity={0.7}
|
|
className={`rounded-2xl p-4 ${config.bgColor} ${config.borderColor} border shadow-sm`}
|
|
>
|
|
<View className="flex-row items-start gap-3">
|
|
{/* Icon Container */}
|
|
<View
|
|
className={`w-12 h-12 rounded-xl items-center justify-center ${config.iconBgColor}`}
|
|
>
|
|
<Ionicons name={config.icon} size={24} color={config.iconColor} />
|
|
</View>
|
|
|
|
{/* Content */}
|
|
<View className="flex-1">
|
|
{/* Header: Ship name + Badge */}
|
|
<View className="flex-row items-center justify-between mb-1">
|
|
<ThemedText className="text-base font-bold text-gray-800 flex-1 mr-2">
|
|
{alarm.ship_name || alarm.thing_id}
|
|
</ThemedText>
|
|
<View className={`px-2 py-1 rounded-full ${config.iconBgColor}`}>
|
|
<ThemedText
|
|
className={`text-xs font-semibold ${config.labelColor}`}
|
|
>
|
|
{config.label}
|
|
</ThemedText>
|
|
</View>
|
|
</View>
|
|
|
|
{/* Zone Info */}
|
|
<ThemedText className="text-xs text-gray-600 mb-2" numberOfLines={2}>
|
|
{alarm.zone.message || alarm.zone.zone_name}
|
|
</ThemedText>
|
|
|
|
{/* Footer: Zone ID + Time */}
|
|
<View className="flex-row items-center justify-between">
|
|
<View className="flex-row items-center gap-1">
|
|
<Ionicons name="time-outline" size={20} color="#6B7280" />
|
|
<ThemedText className="text-xs text-gray-500">
|
|
{formatTimestamp(alarm.zone.gps_time)}
|
|
</ThemedText>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
</TouchableOpacity>
|
|
);
|
|
};
|
|
|
|
// ============ Main Component ============
|
|
interface AlarmListProps {
|
|
data: AlarmData[];
|
|
onPress?: (alarm: AlarmData) => void;
|
|
}
|
|
|
|
export default function AlarmList({ data, onPress }: AlarmListProps) {
|
|
const renderItem = useCallback(
|
|
({ item }: { item: AlarmData }) => (
|
|
<AlarmCard alarm={item} onPress={() => onPress?.(item)} />
|
|
),
|
|
[onPress]
|
|
);
|
|
|
|
const keyExtractor = useCallback(
|
|
(item: AlarmData, index: number) => `${item.thing_id}-${index}`,
|
|
[]
|
|
);
|
|
|
|
const ItemSeparator = useCallback(() => <View className="h-3" />, []);
|
|
|
|
return (
|
|
<FlatList
|
|
data={data}
|
|
renderItem={renderItem}
|
|
keyExtractor={keyExtractor}
|
|
ItemSeparatorComponent={ItemSeparator}
|
|
contentContainerStyle={{ padding: 16 }}
|
|
showsVerticalScrollIndicator={false}
|
|
/>
|
|
);
|
|
}
|