198 lines
5.2 KiB
TypeScript
198 lines
5.2 KiB
TypeScript
import { Ionicons } from "@expo/vector-icons";
|
|
import dayjs from "dayjs";
|
|
import { FlatList, Text, TouchableOpacity, View } from "react-native";
|
|
|
|
export type AlarmStatus = "confirmed" | "pending";
|
|
|
|
export interface AlarmListItem {
|
|
id: string;
|
|
code: string;
|
|
title: string;
|
|
station: string;
|
|
timestamp: number;
|
|
level: 1 | 2 | 3; // 1: warning (yellow), 2: caution (orange/yellow), 3: danger (red)
|
|
status: AlarmStatus;
|
|
}
|
|
|
|
type AlarmProp = {
|
|
alarmsData: AlarmListItem[];
|
|
onPress?: (alarm: AlarmListItem) => void;
|
|
};
|
|
|
|
const AlarmList = ({ alarmsData, onPress }: AlarmProp) => {
|
|
return (
|
|
<FlatList
|
|
data={alarmsData}
|
|
contentContainerStyle={{ paddingHorizontal: 16, paddingVertical: 8 }}
|
|
ItemSeparatorComponent={() => <View className="h-3" />}
|
|
renderItem={({ item }) => (
|
|
<AlarmCard alarm={item} onPress={() => onPress?.(item)} />
|
|
)}
|
|
keyExtractor={(item) => item.id}
|
|
showsVerticalScrollIndicator={false}
|
|
/>
|
|
);
|
|
};
|
|
|
|
type AlarmCardProps = {
|
|
alarm: AlarmListItem;
|
|
onPress?: () => void;
|
|
};
|
|
|
|
const AlarmCard = ({ alarm, onPress }: AlarmCardProps) => {
|
|
const { bgColor, borderColor, iconColor, iconBgColor } = getColorsByLevel(
|
|
alarm.level
|
|
);
|
|
const statusConfig = getStatusConfig(alarm.status);
|
|
|
|
return (
|
|
<TouchableOpacity
|
|
onPress={onPress}
|
|
activeOpacity={0.7}
|
|
className={`rounded-xl p-4 ${bgColor} ${borderColor} border`}
|
|
>
|
|
<View className="flex-row justify-between items-start">
|
|
{/* Left content */}
|
|
<View className="flex-row flex-1">
|
|
{/* Icon */}
|
|
<View
|
|
className={`w-10 h-10 rounded-full items-center justify-center mr-3 ${iconBgColor}`}
|
|
>
|
|
<Ionicons
|
|
name={getIconByLevel(alarm.level)}
|
|
size={20}
|
|
color={iconColor}
|
|
/>
|
|
</View>
|
|
|
|
{/* Info */}
|
|
<View className="flex-1">
|
|
{/* Code */}
|
|
<Text
|
|
className={`text-xs font-medium mb-1 ${getCodeTextColor(
|
|
alarm.level
|
|
)}`}
|
|
>
|
|
{alarm.code}
|
|
</Text>
|
|
|
|
{/* Title */}
|
|
<Text className="text-base font-semibold text-gray-800 mb-2">
|
|
{alarm.title}
|
|
</Text>
|
|
|
|
{/* Station and Time */}
|
|
<View className="flex-row">
|
|
<View className="mr-6">
|
|
<Text className="text-xs text-gray-400 mb-0.5">Trạm</Text>
|
|
<Text className="text-sm text-gray-600">{alarm.station}</Text>
|
|
</View>
|
|
<View>
|
|
<Text className="text-xs text-gray-400 mb-0.5">Thời gian</Text>
|
|
<Text className="text-sm text-gray-600">
|
|
{formatTimestamp(alarm.timestamp)}
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
|
|
{/* Status Badge */}
|
|
{/* <View className="mt-3">
|
|
<View
|
|
className={`self-start px-3 py-1.5 rounded-full ${statusConfig.bgColor}`}
|
|
>
|
|
<Text
|
|
className={`text-xs font-medium ${statusConfig.textColor}`}
|
|
>
|
|
{statusConfig.label}
|
|
</Text>
|
|
</View>
|
|
</View> */}
|
|
</View>
|
|
</View>
|
|
|
|
{/* Checkmark for confirmed */}
|
|
{/* {alarm.status === "confirmed" && (
|
|
<View className="w-6 h-6 rounded-full bg-green-500 items-center justify-center">
|
|
<Ionicons name="checkmark" size={16} color="white" />
|
|
</View>
|
|
)} */}
|
|
</View>
|
|
</TouchableOpacity>
|
|
);
|
|
};
|
|
|
|
const getColorsByLevel = (level: number) => {
|
|
switch (level) {
|
|
case 3: // Danger - Red
|
|
return {
|
|
bgColor: "bg-red-50",
|
|
borderColor: "border-red-200",
|
|
iconColor: "#DC2626",
|
|
iconBgColor: "bg-red-100",
|
|
};
|
|
case 2: // Caution - Yellow/Orange
|
|
return {
|
|
bgColor: "bg-yellow-50",
|
|
borderColor: "border-yellow-200",
|
|
iconColor: "#CA8A04",
|
|
iconBgColor: "bg-yellow-100",
|
|
};
|
|
case 1: // Info - Green
|
|
default:
|
|
return {
|
|
bgColor: "bg-green-50",
|
|
borderColor: "border-green-200",
|
|
iconColor: "#16A34A",
|
|
iconBgColor: "bg-green-100",
|
|
};
|
|
}
|
|
};
|
|
|
|
const getIconByLevel = (level: number): keyof typeof Ionicons.glyphMap => {
|
|
switch (level) {
|
|
case 3:
|
|
return "warning";
|
|
case 2:
|
|
return "alert-circle";
|
|
case 1:
|
|
default:
|
|
return "checkmark-circle";
|
|
}
|
|
};
|
|
|
|
const getCodeTextColor = (level: number) => {
|
|
switch (level) {
|
|
case 3:
|
|
return "text-red-600";
|
|
case 2:
|
|
return "text-yellow-600";
|
|
case 1:
|
|
default:
|
|
return "text-green-600";
|
|
}
|
|
};
|
|
|
|
const getStatusConfig = (status: AlarmStatus) => {
|
|
switch (status) {
|
|
case "confirmed":
|
|
return {
|
|
label: "Đã xác nhận",
|
|
bgColor: "bg-green-100",
|
|
textColor: "text-green-700",
|
|
};
|
|
case "pending":
|
|
default:
|
|
return {
|
|
label: "Chờ xác nhận",
|
|
bgColor: "bg-yellow-100",
|
|
textColor: "text-yellow-700",
|
|
};
|
|
}
|
|
};
|
|
|
|
const formatTimestamp = (timestamp: number) => {
|
|
return dayjs.unix(timestamp).format("YYYY-MM-DD HH:mm");
|
|
};
|
|
|
|
export default AlarmList;
|