import React, { useEffect, useState } from "react"; import { View, Text, StyleSheet, Platform, Image, ActivityIndicator, TouchableOpacity, } from "react-native"; import { Ionicons } from "@expo/vector-icons"; import { useThemeContext } from "@/hooks/use-theme-context"; import { useI18n } from "@/hooks/use-i18n"; import dayjs from "dayjs"; import { queryCrewImage } from "@/controller/TripCrewController"; import { Buffer } from "buffer"; interface CrewCardProps { crew: Model.TripCrews; onEdit?: (crew: Model.TripCrews) => void; onDelete?: (crew: Model.TripCrews) => void; } export default function CrewCard({ crew, onEdit, onDelete }: CrewCardProps) { const { colors } = useThemeContext(); const { t } = useI18n(); const person = crew.Person; const joinedDate = crew.joined_at ? dayjs(crew.joined_at).format("DD/MM/YYYY") : "-"; const leftDate = crew.left_at ? dayjs(crew.left_at).format("DD/MM/YYYY") : null; // State for image const [imageUri, setImageUri] = useState(null); const [imageLoading, setImageLoading] = useState(true); const [imageError, setImageError] = useState(false); // Fetch crew image useEffect(() => { const fetchImage = async () => { if (!person?.personal_id) { setImageLoading(false); setImageError(true); return; } try { const response = await queryCrewImage(person.personal_id); if (response.data) { // Convert arraybuffer to base64 const base64 = Buffer.from(response.data as ArrayBuffer).toString("base64"); setImageUri(`data:image/jpeg;base64,${base64}`); } else { setImageError(true); } } catch (err) { setImageError(true); } finally { setImageLoading(false); } }; fetchImage(); }, [person?.personal_id]); const themedStyles = { card: { backgroundColor: colors.card, borderColor: colors.separator, }, name: { color: colors.text, }, role: { color: colors.primary, backgroundColor: colors.primary + "20", }, label: { color: colors.textSecondary, }, value: { color: colors.text, }, iconColor: colors.textSecondary, imagePlaceholder: { backgroundColor: colors.backgroundSecondary, }, }; return ( {/* Left Image Section (1/3 width) */} {imageLoading ? ( ) : imageUri && !imageError ? ( ) : ( )} {/* Right Content Section (2/3 width) */} {/* Name & Role */} {person?.name || "-"} {crew.role || t("diary.crew.member")} {/* Info Grid */} {/* Phone */} {person?.phone || "-"} {/* Personal ID */} {person?.personal_id || "-"} {/* Joined Date */} {joinedDate} {/* Left Date (only show if exists) */} {leftDate && ( {leftDate} )} {/* Action Buttons */} {(onEdit || onDelete) && ( {onEdit && ( onEdit(crew)} > {t("common.edit")} )} {onDelete && ( onDelete(crew)} > {t("common.delete")} )} )} ); } const styles = StyleSheet.create({ card: { flexDirection: "row", borderRadius: 12, borderWidth: 1, marginBottom: 12, overflow: "hidden", maxHeight: 150, }, imageSection: { width: 130, alignSelf: "stretch", justifyContent: "center", alignItems: "center", }, crewImage: { width: "100%", height: "100%", }, imagePlaceholder: { flex: 1, justifyContent: "center", alignItems: "center", width: "100%", }, contentSection: { flex: 1, padding: 10, justifyContent: "center", }, header: { marginBottom: 8, }, name: { fontSize: 17, fontWeight: "700", marginBottom: 4, fontFamily: Platform.select({ ios: "System", android: "Roboto", default: "System", }), }, roleBadge: { alignSelf: "flex-start", paddingHorizontal: 8, paddingVertical: 2, borderRadius: 10, }, roleText: { fontSize: 12, fontWeight: "600", fontFamily: Platform.select({ ios: "System", android: "Roboto", default: "System", }), }, infoGrid: { gap: 4, }, infoRow: { flexDirection: "row", alignItems: "center", gap: 5, }, value: { fontSize: 14, fontWeight: "500", flex: 1, fontFamily: Platform.select({ ios: "System", android: "Roboto", default: "System", }), }, actionRow: { flexDirection: "row", gap: 8, marginTop: 8, }, actionButton: { flexDirection: "row", alignItems: "center", gap: 4, paddingHorizontal: 10, paddingVertical: 5, borderRadius: 6, }, actionText: { fontSize: 12, fontWeight: "600", fontFamily: Platform.select({ ios: "System", android: "Roboto", default: "System", }), }, });