Cập nhật tab Nhật ký ( CRUD chuyến đi, CRUD thuyền viên trong chuyến đi )
This commit is contained in:
@@ -17,15 +17,14 @@ import {
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { useI18n } from "@/hooks/use-i18n";
|
||||
import { useThemeContext } from "@/hooks/use-theme-context";
|
||||
import {
|
||||
searchCrew,
|
||||
newTripCrew,
|
||||
updateTripCrew,
|
||||
} from "@/controller/TripCrewController";
|
||||
|
||||
import { newTripCrew, updateTripCrew } from "@/controller/TripCrewController";
|
||||
import {
|
||||
newCrew,
|
||||
searchCrew,
|
||||
updateCrewInfo,
|
||||
queryCrewImage,
|
||||
uploadCrewImage,
|
||||
} from "@/controller/CrewController";
|
||||
import * as ImagePicker from "expo-image-picker";
|
||||
import { Buffer } from "buffer";
|
||||
@@ -48,10 +47,11 @@ interface AddEditCrewModalProps {
|
||||
initialData?: Partial<CrewFormData>;
|
||||
tripId?: string; // Required for add mode to add crew to trip
|
||||
existingCrewIds?: string[]; // List of existing crew IDs in trip
|
||||
tripStatus?: number; // Trạng thái chuyến đi để validate (type number)
|
||||
}
|
||||
|
||||
const ROLES = ["captain", "crew"];
|
||||
const DEBOUNCE_DELAY = 1000; // 3 seconds debounce
|
||||
const DEBOUNCE_DELAY = 1000; // 1 seconds debounce
|
||||
|
||||
export default function AddEditCrewModal({
|
||||
visible,
|
||||
@@ -61,6 +61,7 @@ export default function AddEditCrewModal({
|
||||
initialData,
|
||||
tripId,
|
||||
existingCrewIds = [],
|
||||
tripStatus,
|
||||
}: AddEditCrewModalProps) {
|
||||
const { t } = useI18n();
|
||||
const { colors } = useThemeContext();
|
||||
@@ -191,8 +192,24 @@ export default function AddEditCrewModal({
|
||||
phone: person.phone || prev.phone,
|
||||
email: person.email || prev.email,
|
||||
address: person.address || prev.address,
|
||||
note: person.note || prev.note,
|
||||
}));
|
||||
|
||||
// Load avatar của thuyền viên đã tìm thấy
|
||||
try {
|
||||
setIsLoadingImage(true);
|
||||
const imageResponse = await queryCrewImage(personalId.trim());
|
||||
if (imageResponse.data) {
|
||||
const base64 = Buffer.from(
|
||||
imageResponse.data as ArrayBuffer
|
||||
).toString("base64");
|
||||
setImageUri(`data:image/jpeg;base64,${base64}`);
|
||||
}
|
||||
} catch (imageError) {
|
||||
// Không có ảnh - hiển thị placeholder
|
||||
setImageUri(null);
|
||||
} finally {
|
||||
setIsLoadingImage(false);
|
||||
}
|
||||
} else {
|
||||
setSearchStatus("not_found");
|
||||
setFoundPersonData(null);
|
||||
@@ -337,16 +354,18 @@ export default function AddEditCrewModal({
|
||||
try {
|
||||
if (mode === "add" && tripId) {
|
||||
// === CHẾ ĐỘ THÊM MỚI ===
|
||||
const isNewCrew = searchStatus === "not_found" || !foundPersonData;
|
||||
|
||||
// Bước 1: Nếu không tìm thấy thuyền viên trong hệ thống -> tạo mới
|
||||
if (searchStatus === "not_found" || !foundPersonData) {
|
||||
// Note: KHÔNG gửi note vào newCrew vì note là riêng cho từng chuyến đi
|
||||
if (isNewCrew) {
|
||||
await newCrew({
|
||||
personal_id: formData.personalId.trim(),
|
||||
name: formData.name.trim(),
|
||||
phone: formData.phone || "",
|
||||
email: formData.email || "",
|
||||
birth_date: new Date(),
|
||||
note: formData.note || "",
|
||||
note: "", // Không gửi note - note là riêng cho từng chuyến đi
|
||||
address: formData.address || "",
|
||||
});
|
||||
}
|
||||
@@ -361,21 +380,54 @@ export default function AddEditCrewModal({
|
||||
// === CHẾ ĐỘ CHỈNH SỬA ===
|
||||
|
||||
// Bước 1: Cập nhật thông tin cá nhân của thuyền viên (không bao gồm note)
|
||||
await updateCrewInfo(formData.personalId.trim(), {
|
||||
name: formData.name.trim(),
|
||||
phone: formData.phone || "",
|
||||
email: formData.email || "",
|
||||
birth_date: new Date(),
|
||||
address: formData.address || "",
|
||||
});
|
||||
try {
|
||||
await updateCrewInfo(formData.personalId.trim(), {
|
||||
name: formData.name.trim(),
|
||||
phone: formData.phone || "",
|
||||
email: formData.email || "",
|
||||
birth_date: new Date(),
|
||||
address: formData.address || "",
|
||||
});
|
||||
console.log("✅ updateCrewInfo thành công");
|
||||
} catch (crewError: any) {
|
||||
console.error("❌ Lỗi updateCrewInfo:", crewError.response?.data);
|
||||
}
|
||||
|
||||
// Bước 2: Cập nhật role và note của thuyền viên trong chuyến đi
|
||||
await updateTripCrew({
|
||||
trip_id: tripId,
|
||||
personal_id: formData.personalId.trim(),
|
||||
role: formData.role as "captain" | "crew",
|
||||
note: formData.note || "",
|
||||
});
|
||||
// Bước 2: Cập nhật role và note (chỉ khi chuyến đi chưa hoàn thành)
|
||||
// TripStatus: 0=created, 1=pending, 2=approved, 3=active, 4=completed, 5=cancelled
|
||||
if (tripStatus !== 4) {
|
||||
try {
|
||||
await updateTripCrew({
|
||||
trip_id: tripId,
|
||||
personal_id: formData.personalId.trim(),
|
||||
role: formData.role as "captain" | "crew",
|
||||
note: formData.note || "",
|
||||
});
|
||||
console.log("✅ updateTripCrew thành công");
|
||||
} catch (tripCrewError: any) {
|
||||
console.error(
|
||||
"❌ Lỗi updateTripCrew:",
|
||||
tripCrewError.response?.data
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Chuyến đi đã hoàn thành - không cho update role/note
|
||||
console.log("⚠️ Chuyến đi đã hoàn thành - bỏ qua updateTripCrew");
|
||||
}
|
||||
}
|
||||
|
||||
// Upload ảnh nếu có ảnh mới được chọn
|
||||
if (newImageUri && formData.personalId.trim()) {
|
||||
try {
|
||||
console.log("📤 Uploading image:", newImageUri);
|
||||
await uploadCrewImage(formData.personalId.trim(), newImageUri);
|
||||
console.log("✅ Upload ảnh thành công");
|
||||
} catch (uploadError: any) {
|
||||
console.error("❌ Lỗi upload ảnh:", uploadError);
|
||||
console.error("Response data:", uploadError.response?.data);
|
||||
console.error("Response status:", uploadError.response?.status);
|
||||
// Không throw error vì thông tin crew đã được lưu thành công
|
||||
}
|
||||
}
|
||||
|
||||
// Gọi callback để reload danh sách
|
||||
@@ -383,6 +435,8 @@ export default function AddEditCrewModal({
|
||||
handleClose();
|
||||
} catch (error: any) {
|
||||
console.error("Lỗi khi lưu thuyền viên:", error);
|
||||
console.error("Response data:", error.response?.data);
|
||||
console.error("Response status:", error.response?.status);
|
||||
Alert.alert(t("common.error"), t("diary.crew.form.saveError"));
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
@@ -507,6 +561,9 @@ export default function AddEditCrewModal({
|
||||
<ScrollView
|
||||
style={styles.content}
|
||||
showsVerticalScrollIndicator={false}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
contentContainerStyle={{ paddingBottom: 100 }}
|
||||
automaticallyAdjustKeyboardInsets={true}
|
||||
>
|
||||
{/* Ảnh thuyền viên */}
|
||||
<View style={styles.photoSection}>
|
||||
@@ -661,21 +718,23 @@ export default function AddEditCrewModal({
|
||||
/>
|
||||
</View>
|
||||
|
||||
{/* Note */}
|
||||
<View style={styles.formGroup}>
|
||||
<Text style={[styles.label, themedStyles.label]}>
|
||||
{t("diary.crew.note")}
|
||||
</Text>
|
||||
<TextInput
|
||||
style={[styles.input, styles.textArea, themedStyles.input]}
|
||||
value={formData.note}
|
||||
onChangeText={(v) => updateField("note", v)}
|
||||
placeholder={t("diary.crew.form.notePlaceholder")}
|
||||
placeholderTextColor={themedStyles.placeholder.color}
|
||||
multiline
|
||||
numberOfLines={3}
|
||||
/>
|
||||
</View>
|
||||
{/* Note - Chỉ hiển thị khi edit, ẩn khi add */}
|
||||
{mode === "edit" && (
|
||||
<View style={styles.formGroup}>
|
||||
<Text style={[styles.label, themedStyles.label]}>
|
||||
{t("diary.crew.note")}
|
||||
</Text>
|
||||
<TextInput
|
||||
style={[styles.input, styles.textArea, themedStyles.input]}
|
||||
value={formData.note}
|
||||
onChangeText={(v) => updateField("note", v)}
|
||||
placeholder={t("diary.crew.form.notePlaceholder")}
|
||||
placeholderTextColor={themedStyles.placeholder.color}
|
||||
multiline
|
||||
numberOfLines={3}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
</ScrollView>
|
||||
|
||||
{/* Footer */}
|
||||
@@ -849,7 +908,8 @@ const styles = StyleSheet.create({
|
||||
footer: {
|
||||
flexDirection: "row",
|
||||
gap: 12,
|
||||
padding: 20,
|
||||
paddingHorizontal: 20,
|
||||
paddingVertical: 20,
|
||||
borderTopWidth: 1,
|
||||
},
|
||||
cancelButton: {
|
||||
|
||||
Reference in New Issue
Block a user