313 lines
8.0 KiB
TypeScript
313 lines
8.0 KiB
TypeScript
import React from "react";
|
|
import {
|
|
View,
|
|
Text,
|
|
StyleSheet,
|
|
TouchableOpacity,
|
|
Platform,
|
|
} from "react-native";
|
|
import { Ionicons } from "@expo/vector-icons";
|
|
import { useTripStatusConfig } from "./types";
|
|
import { useThings } from "@/state/use-thing";
|
|
import dayjs from "dayjs";
|
|
import { useI18n } from "@/hooks/use-i18n";
|
|
import { useThemeContext } from "@/hooks/use-theme-context";
|
|
|
|
interface TripCardProps {
|
|
trip: Model.Trip;
|
|
onPress?: () => void;
|
|
onView?: () => void;
|
|
onEdit?: () => void;
|
|
onTeam?: () => void;
|
|
onSend?: () => void;
|
|
onDelete?: () => void;
|
|
}
|
|
|
|
export default function TripCard({
|
|
trip,
|
|
onPress,
|
|
onView,
|
|
onEdit,
|
|
onTeam,
|
|
onSend,
|
|
onDelete,
|
|
}: TripCardProps) {
|
|
const { t } = useI18n();
|
|
const { colors } = useThemeContext();
|
|
const { things } = useThings();
|
|
const TRIP_STATUS_CONFIG = useTripStatusConfig();
|
|
|
|
// Tìm thing có id trùng với vms_id của trip
|
|
const thingOfTrip: Model.Thing | undefined = things?.find(
|
|
(thing) => thing.id === trip.vms_id
|
|
);
|
|
|
|
// Lấy config status từ trip_status (number)
|
|
const statusKey = trip.trip_status as keyof typeof TRIP_STATUS_CONFIG;
|
|
const statusConfig = TRIP_STATUS_CONFIG[statusKey] || {
|
|
label: "-",
|
|
bgColor: "#eee",
|
|
textColor: "#333",
|
|
icon: "help",
|
|
};
|
|
|
|
// Determine which actions to show based on status
|
|
const showEdit = trip.trip_status === 0 || trip.trip_status === 1;
|
|
const showSend = trip.trip_status === 0;
|
|
const showDelete = trip.trip_status === 1;
|
|
|
|
const themedStyles = {
|
|
card: {
|
|
backgroundColor: colors.card,
|
|
borderColor: colors.border,
|
|
},
|
|
title: {
|
|
color: colors.text,
|
|
},
|
|
label: {
|
|
color: colors.textSecondary,
|
|
},
|
|
value: {
|
|
color: colors.text,
|
|
},
|
|
divider: {
|
|
backgroundColor: colors.separator,
|
|
},
|
|
actionText: {
|
|
color: colors.textSecondary,
|
|
},
|
|
};
|
|
|
|
return (
|
|
<View style={[styles.card, themedStyles.card]}>
|
|
<TouchableOpacity onPress={onPress} activeOpacity={0.7}>
|
|
{/* Header */}
|
|
<View style={styles.header}>
|
|
<View style={styles.headerLeft}>
|
|
<Ionicons
|
|
name={statusConfig.icon as any}
|
|
size={24}
|
|
color={statusConfig.textColor}
|
|
/>
|
|
<View style={styles.titleContainer}>
|
|
<Text style={[styles.title, themedStyles.title]}>{trip.name}</Text>
|
|
</View>
|
|
</View>
|
|
<View
|
|
style={[
|
|
styles.badge,
|
|
{
|
|
backgroundColor: statusConfig.bgColor,
|
|
},
|
|
]}
|
|
>
|
|
<Text
|
|
style={[
|
|
styles.badgeText,
|
|
{
|
|
color: statusConfig.textColor,
|
|
},
|
|
]}
|
|
>
|
|
{statusConfig.label}
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
|
|
{/* Info Grid */}
|
|
<View style={styles.infoGrid}>
|
|
<View style={styles.infoRow}>
|
|
<Text style={[styles.label, themedStyles.label]}>{t("diary.tripCard.shipCode")}</Text>
|
|
<Text style={[styles.value, themedStyles.value]}>
|
|
{thingOfTrip?.metadata?.ship_reg_number /* hoặc trip.ship_id */}
|
|
</Text>
|
|
</View>
|
|
|
|
<View style={styles.infoRow}>
|
|
<Text style={[styles.label, themedStyles.label]}>{t("diary.tripCard.departure")}</Text>
|
|
<Text style={[styles.value, themedStyles.value]}>
|
|
{trip.departure_time
|
|
? dayjs(trip.departure_time).format("DD/MM/YYYY HH:mm")
|
|
: "-"}
|
|
</Text>
|
|
</View>
|
|
|
|
<View style={styles.infoRow}>
|
|
<Text style={[styles.label, themedStyles.label]}>{t("diary.tripCard.return")}</Text>
|
|
{/* FIXME: trip.returnDate không có trong dữ liệu API, cần mapping từ trip.arrival_time */}
|
|
<Text style={[styles.value, themedStyles.value]}>
|
|
{trip.arrival_time
|
|
? dayjs(trip.arrival_time).format("DD/MM/YYYY HH:mm")
|
|
: "-"}
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
</TouchableOpacity>
|
|
|
|
{/* Action Buttons */}
|
|
<View style={[styles.divider, themedStyles.divider]} />
|
|
<View style={styles.actionsContainer}>
|
|
<TouchableOpacity
|
|
style={styles.actionButton}
|
|
onPress={onView}
|
|
activeOpacity={0.7}
|
|
>
|
|
<Ionicons name="eye-outline" size={20} color={colors.textSecondary} />
|
|
<Text style={[styles.actionText, themedStyles.actionText]}>{t("diary.tripCard.view")}</Text>
|
|
</TouchableOpacity>
|
|
|
|
{showEdit && (
|
|
<TouchableOpacity
|
|
style={styles.actionButton}
|
|
onPress={onEdit}
|
|
activeOpacity={0.7}
|
|
>
|
|
<Ionicons name="create-outline" size={20} color={colors.textSecondary} />
|
|
<Text style={[styles.actionText, themedStyles.actionText]}>{t("diary.tripCard.edit")}</Text>
|
|
</TouchableOpacity>
|
|
)}
|
|
|
|
<TouchableOpacity
|
|
style={styles.actionButton}
|
|
onPress={onTeam}
|
|
activeOpacity={0.7}
|
|
>
|
|
<Ionicons name="people-outline" size={20} color={colors.textSecondary} />
|
|
<Text style={[styles.actionText, themedStyles.actionText]}>{t("diary.tripCard.team")}</Text>
|
|
</TouchableOpacity>
|
|
|
|
{showSend && (
|
|
<TouchableOpacity
|
|
style={styles.actionButton}
|
|
onPress={onSend}
|
|
activeOpacity={0.7}
|
|
>
|
|
<Ionicons name="send-outline" size={20} color={colors.textSecondary} />
|
|
<Text style={[styles.actionText, themedStyles.actionText]}>{t("diary.tripCard.send")}</Text>
|
|
</TouchableOpacity>
|
|
)}
|
|
|
|
{showDelete && (
|
|
<TouchableOpacity
|
|
style={styles.actionButton}
|
|
onPress={onDelete}
|
|
activeOpacity={0.7}
|
|
>
|
|
<Ionicons name="trash-outline" size={20} color={colors.error} />
|
|
<Text style={[styles.actionText, styles.deleteText]}>{t("diary.tripCard.delete")}</Text>
|
|
</TouchableOpacity>
|
|
)}
|
|
</View>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
card: {
|
|
borderRadius: 8,
|
|
padding: 12,
|
|
marginBottom: 8,
|
|
shadowColor: "#000",
|
|
shadowOffset: {
|
|
width: 0,
|
|
height: 1,
|
|
},
|
|
shadowOpacity: 0.05,
|
|
shadowRadius: 4,
|
|
elevation: 2,
|
|
borderWidth: 1,
|
|
},
|
|
header: {
|
|
flexDirection: "row",
|
|
justifyContent: "space-between",
|
|
alignItems: "flex-start",
|
|
marginBottom: 8,
|
|
},
|
|
headerLeft: {
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
flex: 1,
|
|
},
|
|
titleContainer: {
|
|
marginLeft: 8,
|
|
flex: 1,
|
|
},
|
|
title: {
|
|
fontSize: 16,
|
|
fontWeight: "600",
|
|
marginBottom: 2,
|
|
fontFamily: Platform.select({
|
|
ios: "System",
|
|
android: "Roboto",
|
|
default: "System",
|
|
}),
|
|
},
|
|
|
|
badge: {
|
|
paddingHorizontal: 8,
|
|
paddingVertical: 2,
|
|
borderRadius: 8,
|
|
},
|
|
badgeText: {
|
|
fontSize: 12,
|
|
fontWeight: "500",
|
|
fontFamily: Platform.select({
|
|
ios: "System",
|
|
android: "Roboto",
|
|
default: "System",
|
|
}),
|
|
},
|
|
infoGrid: {
|
|
gap: 6,
|
|
marginBottom: 8,
|
|
},
|
|
infoRow: {
|
|
flexDirection: "row",
|
|
justifyContent: "space-between",
|
|
paddingVertical: 4,
|
|
},
|
|
label: {
|
|
fontSize: 14,
|
|
fontFamily: Platform.select({
|
|
ios: "System",
|
|
android: "Roboto",
|
|
default: "System",
|
|
}),
|
|
},
|
|
value: {
|
|
fontSize: 14,
|
|
fontWeight: "500",
|
|
fontFamily: Platform.select({
|
|
ios: "System",
|
|
android: "Roboto",
|
|
default: "System",
|
|
}),
|
|
},
|
|
divider: {
|
|
height: 1,
|
|
marginVertical: 8,
|
|
},
|
|
actionsContainer: {
|
|
flexDirection: "row",
|
|
justifyContent: "space-around",
|
|
alignItems: "center",
|
|
},
|
|
actionButton: {
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
gap: 4,
|
|
paddingVertical: 4,
|
|
},
|
|
actionText: {
|
|
fontSize: 14,
|
|
fontFamily: Platform.select({
|
|
ios: "System",
|
|
android: "Roboto",
|
|
default: "System",
|
|
}),
|
|
},
|
|
deleteText: {
|
|
color: "#EF4444",
|
|
},
|
|
});
|