Cập nhật theme cho cảnh báo, giám sát
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { AlarmData } from "@/app/(tabs)";
|
||||
import { ThemedText } from "@/components/themed-text";
|
||||
import { useAppTheme } from "@/hooks/use-app-theme";
|
||||
import { formatTimestamp } from "@/services/time_service";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { useCallback } from "react";
|
||||
@@ -14,61 +15,66 @@ interface AlarmCardProps {
|
||||
}
|
||||
|
||||
// ============ Config ============
|
||||
const ALARM_CONFIG: Record<
|
||||
AlarmType,
|
||||
{
|
||||
icon: keyof typeof Ionicons.glyphMap;
|
||||
label: string;
|
||||
bgColor: string;
|
||||
borderColor: string;
|
||||
iconBgColor: string;
|
||||
iconColor: string;
|
||||
labelColor: string;
|
||||
}
|
||||
> = {
|
||||
const getAlarmConfig = (isDark: boolean) => ({
|
||||
entered: {
|
||||
icon: "warning",
|
||||
icon: "warning" as keyof typeof Ionicons.glyphMap,
|
||||
label: "Xâm nhập",
|
||||
bgColor: "bg-red-50",
|
||||
borderColor: "border-red-200",
|
||||
iconBgColor: "bg-red-100",
|
||||
iconColor: "#DC2626",
|
||||
labelColor: "text-red-600",
|
||||
bgColor: isDark ? "rgba(220, 38, 38, 0.15)" : "#FEF2F2",
|
||||
borderColor: isDark ? "rgba(220, 38, 38, 0.3)" : "#FECACA",
|
||||
iconBgColor: isDark ? "rgba(220, 38, 38, 0.25)" : "#FEE2E2",
|
||||
iconColor: isDark ? "#F87171" : "#DC2626",
|
||||
labelColor: isDark ? "#F87171" : "#DC2626",
|
||||
},
|
||||
approaching: {
|
||||
icon: "alert-circle",
|
||||
icon: "alert-circle" as keyof typeof Ionicons.glyphMap,
|
||||
label: "Tiếp cận",
|
||||
bgColor: "bg-amber-50",
|
||||
borderColor: "border-amber-200",
|
||||
iconBgColor: "bg-amber-100",
|
||||
iconColor: "#D97706",
|
||||
labelColor: "text-amber-600",
|
||||
bgColor: isDark ? "rgba(217, 119, 6, 0.15)" : "#FFFBEB",
|
||||
borderColor: isDark ? "rgba(217, 119, 6, 0.3)" : "#FDE68A",
|
||||
iconBgColor: isDark ? "rgba(217, 119, 6, 0.25)" : "#FEF3C7",
|
||||
iconColor: isDark ? "#FBBF24" : "#D97706",
|
||||
labelColor: isDark ? "#FBBF24" : "#D97706",
|
||||
},
|
||||
fishing: {
|
||||
icon: "fish",
|
||||
icon: "fish" as keyof typeof Ionicons.glyphMap,
|
||||
label: "Đánh bắt",
|
||||
bgColor: "bg-orange-50",
|
||||
borderColor: "border-orange-200",
|
||||
iconBgColor: "bg-orange-100",
|
||||
iconColor: "#EA580C",
|
||||
labelColor: "text-orange-600",
|
||||
bgColor: isDark ? "rgba(234, 88, 12, 0.15)" : "#FFF7ED",
|
||||
borderColor: isDark ? "rgba(234, 88, 12, 0.3)" : "#FED7AA",
|
||||
iconBgColor: isDark ? "rgba(234, 88, 12, 0.25)" : "#FFEDD5",
|
||||
iconColor: isDark ? "#FB923C" : "#EA580C",
|
||||
labelColor: isDark ? "#FB923C" : "#EA580C",
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
// ============ AlarmCard Component ============
|
||||
const AlarmCard = ({ alarm, onPress }: AlarmCardProps) => {
|
||||
const config = ALARM_CONFIG[alarm.type];
|
||||
const { colors, utils } = useAppTheme();
|
||||
const isDark = utils.isDark;
|
||||
const alarmConfig = getAlarmConfig(isDark);
|
||||
const config = alarmConfig[alarm.type];
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress={onPress}
|
||||
activeOpacity={0.7}
|
||||
className={`rounded-2xl p-4 ${config.bgColor} ${config.borderColor} border shadow-sm`}
|
||||
style={{
|
||||
borderRadius: 16,
|
||||
padding: 16,
|
||||
backgroundColor: config.bgColor,
|
||||
borderWidth: 1,
|
||||
borderColor: config.borderColor,
|
||||
}}
|
||||
>
|
||||
<View className="flex-row items-start gap-3">
|
||||
{/* Icon Container */}
|
||||
<View
|
||||
className={`w-12 h-12 rounded-xl items-center justify-center ${config.iconBgColor}`}
|
||||
style={{
|
||||
width: 48,
|
||||
height: 48,
|
||||
borderRadius: 12,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
backgroundColor: config.iconBgColor,
|
||||
}}
|
||||
>
|
||||
<Ionicons name={config.icon} size={24} color={config.iconColor} />
|
||||
</View>
|
||||
@@ -77,12 +83,31 @@ const AlarmCard = ({ alarm, onPress }: AlarmCardProps) => {
|
||||
<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">
|
||||
<ThemedText
|
||||
style={{
|
||||
fontSize: 16,
|
||||
fontWeight: "700",
|
||||
color: colors.text,
|
||||
flex: 1,
|
||||
marginRight: 8,
|
||||
}}
|
||||
>
|
||||
{alarm.ship_name || alarm.thing_id}
|
||||
</ThemedText>
|
||||
<View className={`px-2 py-1 rounded-full ${config.iconBgColor}`}>
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: 8,
|
||||
paddingVertical: 4,
|
||||
borderRadius: 9999,
|
||||
backgroundColor: config.iconBgColor,
|
||||
}}
|
||||
>
|
||||
<ThemedText
|
||||
className={`text-xs font-semibold ${config.labelColor}`}
|
||||
style={{
|
||||
fontSize: 12,
|
||||
fontWeight: "600",
|
||||
color: config.labelColor,
|
||||
}}
|
||||
>
|
||||
{config.label}
|
||||
</ThemedText>
|
||||
@@ -90,15 +115,27 @@ const AlarmCard = ({ alarm, onPress }: AlarmCardProps) => {
|
||||
</View>
|
||||
|
||||
{/* Zone Info */}
|
||||
<ThemedText className="text-xs text-gray-600 mb-2" numberOfLines={2}>
|
||||
<ThemedText
|
||||
style={{
|
||||
fontSize: 12,
|
||||
color: colors.textSecondary,
|
||||
marginBottom: 8,
|
||||
}}
|
||||
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">
|
||||
<Ionicons name="time-outline" size={20} color={colors.icon} />
|
||||
<ThemedText
|
||||
style={{
|
||||
fontSize: 12,
|
||||
color: colors.textSecondary,
|
||||
}}
|
||||
>
|
||||
{formatTimestamp(alarm.zone.gps_time)}
|
||||
</ThemedText>
|
||||
</View>
|
||||
|
||||
@@ -13,28 +13,31 @@ interface ShipInfoProps {
|
||||
|
||||
const ShipInfo = ({ thingMetadata }: ShipInfoProps) => {
|
||||
const { t } = useI18n();
|
||||
const { colors } = useAppTheme();
|
||||
// Định nghĩa màu sắc theo trạng thái
|
||||
const { colors, utils } = useAppTheme();
|
||||
const isDark = utils.isDark;
|
||||
|
||||
// Định nghĩa màu sắc theo trạng thái - hỗ trợ cả light và dark theme
|
||||
const statusConfig = {
|
||||
normal: {
|
||||
dotColor: "bg-green-500",
|
||||
badgeColor: "bg-green-100",
|
||||
badgeTextColor: "text-green-700",
|
||||
dotColor: colors.success,
|
||||
badgeColor: isDark ? "rgba(52, 199, 89, 0.2)" : "#dcfce7",
|
||||
badgeTextColor: isDark ? "#30D158" : "#15803d",
|
||||
badgeText: "Bình thường",
|
||||
},
|
||||
warning: {
|
||||
dotColor: "bg-yellow-500",
|
||||
badgeColor: "bg-yellow-100",
|
||||
badgeTextColor: "text-yellow-700",
|
||||
dotColor: colors.warning,
|
||||
badgeColor: isDark ? "rgba(255, 102, 0, 0.2)" : "#fef3c7",
|
||||
badgeTextColor: isDark ? "#ff9500" : "#b45309",
|
||||
badgeText: "Cảnh báo",
|
||||
},
|
||||
danger: {
|
||||
dotColor: "bg-red-500",
|
||||
badgeColor: "bg-red-100",
|
||||
badgeTextColor: "text-red-700",
|
||||
dotColor: colors.error,
|
||||
badgeColor: isDark ? "rgba(255, 69, 58, 0.2)" : "#fee2e2",
|
||||
badgeTextColor: isDark ? "#FF453A" : "#b91c1c",
|
||||
badgeText: "Nguy hiểm",
|
||||
},
|
||||
};
|
||||
|
||||
const getThingStatus = () => {
|
||||
switch (thingMetadata?.state_level) {
|
||||
case STATUS_NORMAL:
|
||||
@@ -67,57 +70,82 @@ const ShipInfo = ({ thingMetadata }: ShipInfoProps) => {
|
||||
<View className="flex-row items-center justify-between mb-3">
|
||||
<View className="flex-row items-center gap-2">
|
||||
{/* Status dot */}
|
||||
<View className={`h-3 w-3 rounded-full ${currentStatus.dotColor}`} />
|
||||
<View
|
||||
style={{
|
||||
height: 12,
|
||||
width: 12,
|
||||
borderRadius: 6,
|
||||
backgroundColor: currentStatus.dotColor,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Tên tàu */}
|
||||
<Text className="text-lg font-semibold text-gray-900">
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 18,
|
||||
fontWeight: "600",
|
||||
color: colors.text,
|
||||
}}
|
||||
>
|
||||
{thingMetadata?.ship_name}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{/* Badge trạng thái */}
|
||||
<View className={`px-3 py-1 rounded-full ${currentStatus.badgeColor}`}>
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 4,
|
||||
borderRadius: 9999,
|
||||
backgroundColor: currentStatus.badgeColor,
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
className={`text-md font-medium ${currentStatus.badgeTextColor}`}
|
||||
style={{
|
||||
fontSize: 14,
|
||||
fontWeight: "500",
|
||||
color: currentStatus.badgeTextColor,
|
||||
}}
|
||||
>
|
||||
{currentStatus.badgeText}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Mã tàu */}
|
||||
{/* <Text className="text-base text-gray-600 mb-2">{shipCode}</Text> */}
|
||||
{/* Tốc độ và hướng */}
|
||||
<View className="flex-row items-center justify-between gap-2 mb-3">
|
||||
<View className="flex-row items-center gap-2 w-2/3">
|
||||
<Ionicons name="speedometer-outline" size={16} color="#6B7280" />
|
||||
<Text className="text-md text-gray-600">
|
||||
<Ionicons name="speedometer-outline" size={16} color={colors.icon} />
|
||||
<Text style={{ fontSize: 14, color: colors.textSecondary }}>
|
||||
{kmhToKnot(gpsData.s || 0)} {t("home.speed_units")}
|
||||
</Text>
|
||||
</View>
|
||||
<View className="flex-row items-start gap-2 w-1/3 ">
|
||||
<Ionicons name="compass-outline" size={16} color="#6B7280" />
|
||||
<Text className="text-md text-gray-600">{gpsData.h}°</Text>
|
||||
<Ionicons name="compass-outline" size={16} color={colors.icon} />
|
||||
<Text style={{ fontSize: 14, color: colors.textSecondary }}>
|
||||
{gpsData.h}°
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Tọa độ */}
|
||||
<View className="flex-row items-center justify-between gap-2 mb-2">
|
||||
<View className="flex-row items-center gap-2 w-2/3">
|
||||
<Ionicons name="location-outline" size={16} color="#6B7280" />
|
||||
<Text className="text-md text-gray-600">
|
||||
<Ionicons name="location-outline" size={16} color={colors.icon} />
|
||||
<Text style={{ fontSize: 14, color: colors.textSecondary }}>
|
||||
{convertToDMS(gpsData.lat || 0, true)},
|
||||
{convertToDMS(gpsData.lon || 0, false)}
|
||||
</Text>
|
||||
</View>
|
||||
<View className=" flex-row items-start gap-2 w-1/3 ">
|
||||
<Ionicons name="time-outline" size={16} color="#6B7280" />
|
||||
<Text className="text-md text-gray-600">
|
||||
<Ionicons name="time-outline" size={16} color={colors.icon} />
|
||||
<Text style={{ fontSize: 14, color: colors.textSecondary }}>
|
||||
{formatRelativeTime(gpsData.t)}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
{/* <View className="flex">
|
||||
<Description title="Trạng thái" description={thingMetadata?.state} />
|
||||
</View> */}
|
||||
|
||||
{/* Trạng thái */}
|
||||
{thingMetadata?.state !== "" && (
|
||||
<View className="flex-row items-center gap-2">
|
||||
<ThemedText style={{ color: colors.textSecondary, fontSize: 15 }}>
|
||||
|
||||
Reference in New Issue
Block a user