Khởi tạo ban đầu

This commit is contained in:
Tran Anh Tuan
2025-11-28 16:59:57 +07:00
parent 2911be97b2
commit 4ba46a7df2
131 changed files with 28066 additions and 0 deletions

View File

@@ -0,0 +1,245 @@
import { IconSymbol } from "@/components/ui/icon-symbol";
import { useI18n } from "@/hooks/use-i18n";
import { useThemeContext } from "@/hooks/use-theme-context";
import React, { useEffect, useState } from "react";
import {
KeyboardAvoidingView,
Modal,
Platform,
ScrollView,
Text,
TextInput,
TouchableOpacity,
View,
} from "react-native";
import { createStyles } from "./style/TripCostDetailModal.styles";
// ---------------------------
// 🧩 Interface
// ---------------------------
interface TripCostDetailModalProps {
visible: boolean;
onClose: () => void;
data: Model.TripCost[];
}
// ---------------------------
// 💰 Component Modal
// ---------------------------
const TripCostDetailModal: React.FC<TripCostDetailModalProps> = ({
visible,
onClose,
data,
}) => {
const { t } = useI18n();
const { colors } = useThemeContext();
const styles = React.useMemo(() => createStyles(colors), [colors]);
const [isEditing, setIsEditing] = useState(false);
const [editableData, setEditableData] = useState<Model.TripCost[]>(data);
// Cập nhật editableData khi props data thay đổi (API fetch xong)
useEffect(() => {
setEditableData(data);
}, [data]);
const tongCong = editableData.reduce((sum, item) => sum + item.total_cost, 0);
const handleEdit = () => {
setIsEditing(!isEditing);
};
const handleSave = () => {
setIsEditing(false);
// TODO: Save data to backend
console.log("Saved data:", editableData);
};
const handleCancel = () => {
setIsEditing(false);
setEditableData(data); // Reset to original data
};
const updateItem = (
index: number,
field: keyof Model.TripCost,
value: string
) => {
setEditableData((prev) =>
prev.map((item, idx) => {
if (idx === index) {
const updated = { ...item, [field]: value };
// Recalculate total_cost
if (field === "amount" || field === "cost_per_unit") {
const amount =
Number(field === "amount" ? value : item.amount) || 0;
const costPerUnit =
Number(field === "cost_per_unit" ? value : item.cost_per_unit) ||
0;
updated.total_cost = amount * costPerUnit;
}
return updated;
}
return item;
})
);
};
return (
<Modal
visible={visible}
animationType="slide"
presentationStyle="pageSheet"
onRequestClose={onClose}
>
<KeyboardAvoidingView
style={{ flex: 1 }}
behavior={Platform.OS === "ios" ? "padding" : "height"}
keyboardVerticalOffset={60}
>
<View style={styles.container}>
{/* Header */}
<View style={styles.header}>
<Text style={styles.title}>{t("trip.costDetailModal.title")}</Text>
<View style={styles.headerButtons}>
{isEditing ? (
<>
<TouchableOpacity
onPress={handleCancel}
style={styles.cancelButton}
>
<Text style={styles.cancelButtonText}>
{t("trip.costDetailModal.cancel")}
</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={handleSave}
style={styles.saveButton}
>
<Text style={styles.saveButtonText}>
{t("trip.costDetailModal.save")}
</Text>
</TouchableOpacity>
</>
) : (
<TouchableOpacity
onPress={handleEdit}
style={styles.editButton}
>
<View style={styles.editIconButton}>
<IconSymbol
name="pencil"
size={28}
color="#fff"
weight="heavy"
/>
</View>
</TouchableOpacity>
)}
<TouchableOpacity onPress={onClose} style={styles.closeButton}>
<View style={styles.closeIconButton}>
<IconSymbol name="xmark" size={28} color="#fff" />
</View>
</TouchableOpacity>
</View>
</View>
{/* Content */}
<ScrollView style={styles.content}>
{editableData.map((item, index) => (
<View key={index} style={styles.itemCard}>
{/* Loại */}
<View style={styles.fieldGroup}>
<Text style={styles.label}>
{t("trip.costDetailModal.costType")}
</Text>
<TextInput
style={[styles.input, !isEditing && styles.inputDisabled]}
value={item.type}
onChangeText={(value) => updateItem(index, "type", value)}
editable={isEditing}
placeholder={t("trip.costDetailModal.enterCostType")}
/>
</View>
{/* Số lượng & Đơn vị */}
<View style={styles.rowGroup}>
<View
style={[styles.fieldGroup, { flex: 1, marginRight: 8 }]}
>
<Text style={styles.label}>
{t("trip.costDetailModal.quantity")}
</Text>
<TextInput
style={[styles.input, !isEditing && styles.inputDisabled]}
value={String(item.amount ?? "")}
onChangeText={(value) =>
updateItem(index, "amount", value)
}
editable={isEditing}
keyboardType="numeric"
placeholder="0"
/>
</View>
<View style={[styles.fieldGroup, { flex: 1, marginLeft: 8 }]}>
<Text style={styles.label}>
{t("trip.costDetailModal.unit")}
</Text>
<TextInput
style={[styles.input, !isEditing && styles.inputDisabled]}
value={item.unit}
onChangeText={(value) => updateItem(index, "unit", value)}
editable={isEditing}
placeholder={t("trip.costDetailModal.placeholder")}
/>
</View>
</View>
{/* Chi phí/đơn vị */}
<View style={styles.fieldGroup}>
<Text style={styles.label}>
{t("trip.costDetailModal.costPerUnit")}
</Text>
<TextInput
style={[styles.input, !isEditing && styles.inputDisabled]}
value={String(item.cost_per_unit ?? "")}
onChangeText={(value) =>
updateItem(index, "cost_per_unit", value)
}
editable={isEditing}
keyboardType="numeric"
placeholder="0"
/>
</View>
{/* Tổng chi phí */}
<View style={styles.fieldGroup}>
<Text style={styles.label}>
{t("trip.costDetailModal.totalCost")}
</Text>
<View style={styles.totalContainer}>
<Text style={styles.totalText}>
{item.total_cost.toLocaleString()}{" "}
{t("trip.costDetailModal.vnd")}
</Text>
</View>
</View>
</View>
))}
{/* Footer Total */}
<View style={styles.footerTotal}>
<Text style={styles.footerLabel}>
{t("trip.costDetailModal.total")}
</Text>
<Text style={styles.footerAmount}>
{tongCong.toLocaleString()} {t("trip.costDetailModal.vnd")}
</Text>
</View>
</ScrollView>
</View>
</KeyboardAvoidingView>
</Modal>
);
};
export default TripCostDetailModal;