312 lines
7.9 KiB
TypeScript
312 lines
7.9 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
import {
|
|
Platform,
|
|
ScrollView,
|
|
StyleSheet,
|
|
Text,
|
|
TouchableOpacity,
|
|
View,
|
|
} from "react-native";
|
|
import { SafeAreaView } from "react-native-safe-area-context";
|
|
import { Ionicons } from "@expo/vector-icons";
|
|
import FilterButton from "@/components/diary/FilterButton";
|
|
import TripCard from "@/components/diary/TripCard";
|
|
import FilterModal, { FilterValues } from "@/components/diary/FilterModal";
|
|
import { useThings } from "@/state/use-thing";
|
|
import { useTripsList } from "@/state/use-tripslist";
|
|
import dayjs from "dayjs";
|
|
import { useI18n } from "@/hooks/use-i18n";
|
|
import { useThemeContext } from "@/hooks/use-theme-context";
|
|
|
|
export default function diary() {
|
|
const { t } = useI18n();
|
|
const { colors } = useThemeContext();
|
|
const [showFilterModal, setShowFilterModal] = useState(false);
|
|
const [filters, setFilters] = useState<FilterValues>({
|
|
status: null,
|
|
startDate: null,
|
|
endDate: null,
|
|
selectedShip: null, // Tàu được chọn
|
|
});
|
|
|
|
// Body call API things (đang fix cứng)
|
|
const payloadThings: Model.SearchThingBody = {
|
|
offset: 0,
|
|
limit: 200,
|
|
order: "name",
|
|
dir: "asc",
|
|
metadata: {
|
|
not_empty: "ship_name, ship_reg_number",
|
|
},
|
|
};
|
|
|
|
// Gọi API things
|
|
const { getThings } = useThings();
|
|
useEffect(() => {
|
|
getThings(payloadThings);
|
|
}, []);
|
|
|
|
// State cho payload trips
|
|
const [payloadTrips, setPayloadTrips] = useState<Model.TripListBody>({
|
|
name: "",
|
|
order: "",
|
|
dir: "desc",
|
|
offset: 0,
|
|
limit: 10,
|
|
metadata: {
|
|
from: "",
|
|
to: "",
|
|
ship_name: "",
|
|
reg_number: "",
|
|
province_code: "",
|
|
owner_id: "",
|
|
ship_id: "",
|
|
status: "",
|
|
},
|
|
});
|
|
|
|
const { tripsList, getTripsList } = useTripsList();
|
|
|
|
// Gọi API trips lần đầu
|
|
useEffect(() => {
|
|
getTripsList(payloadTrips);
|
|
}, []);
|
|
|
|
// Gọi lại API khi payload thay đổi (do filter)
|
|
useEffect(() => {
|
|
getTripsList(payloadTrips);
|
|
console.log("Payload trips:", payloadTrips);
|
|
}, [payloadTrips]);
|
|
|
|
const handleFilter = () => {
|
|
setShowFilterModal(true);
|
|
};
|
|
|
|
const handleApplyFilters = (newFilters: FilterValues) => {
|
|
setFilters(newFilters);
|
|
|
|
// Cập nhật payload với filter mới
|
|
// Lưu ý: status gửi lên server là string
|
|
const updatedPayload: Model.TripListBody = {
|
|
...payloadTrips,
|
|
metadata: {
|
|
...payloadTrips.metadata,
|
|
from: newFilters.startDate
|
|
? dayjs(newFilters.startDate).startOf("day").toISOString()
|
|
: "",
|
|
to: newFilters.endDate
|
|
? dayjs(newFilters.endDate).endOf("day").toISOString()
|
|
: "",
|
|
// Convert number status sang string để gửi lên server
|
|
status: newFilters.status !== null ? String(newFilters.status) : "",
|
|
// Thêm ship_id từ tàu đã chọn
|
|
ship_name: newFilters.selectedShip?.shipName || "",
|
|
},
|
|
};
|
|
|
|
setPayloadTrips(updatedPayload);
|
|
setShowFilterModal(false);
|
|
};
|
|
|
|
const handleTripPress = (tripId: string) => {
|
|
// TODO: Navigate to trip detail
|
|
console.log("Trip pressed:", tripId);
|
|
};
|
|
|
|
const handleViewTrip = (tripId: string) => {
|
|
console.log("View trip:", tripId);
|
|
// TODO: Navigate to trip detail view
|
|
};
|
|
|
|
const handleEditTrip = (tripId: string) => {
|
|
console.log("Edit trip:", tripId);
|
|
// TODO: Navigate to trip edit screen
|
|
};
|
|
|
|
const handleViewTeam = (tripId: string) => {
|
|
console.log("View team:", tripId);
|
|
// TODO: Navigate to team management
|
|
};
|
|
|
|
const handleSendTrip = (tripId: string) => {
|
|
console.log("Send trip:", tripId);
|
|
// TODO: Send trip for approval
|
|
};
|
|
|
|
const handleDeleteTrip = (tripId: string) => {
|
|
console.log("Delete trip:", tripId);
|
|
// TODO: Show confirmation dialog and delete trip
|
|
};
|
|
|
|
// Dynamic styles based on theme
|
|
const themedStyles = {
|
|
safeArea: {
|
|
backgroundColor: colors.background,
|
|
},
|
|
titleText: {
|
|
color: colors.text,
|
|
},
|
|
countText: {
|
|
color: colors.textSecondary,
|
|
},
|
|
addButton: {
|
|
backgroundColor: colors.primary,
|
|
},
|
|
emptyText: {
|
|
color: colors.textSecondary,
|
|
},
|
|
};
|
|
|
|
return (
|
|
<SafeAreaView style={[styles.safeArea, themedStyles.safeArea]} edges={["top"]}>
|
|
<View style={styles.container}>
|
|
{/* Header */}
|
|
<Text style={[styles.titleText, themedStyles.titleText]}>{t("diary.title")}</Text>
|
|
|
|
{/* Filter & Add Button Row */}
|
|
<View style={styles.actionRow}>
|
|
<FilterButton
|
|
onPress={handleFilter}
|
|
isFiltered={
|
|
filters.status !== null ||
|
|
filters.startDate !== null ||
|
|
filters.endDate !== null ||
|
|
filters.selectedShip !== null
|
|
}
|
|
/>
|
|
<TouchableOpacity
|
|
style={[styles.addButton, themedStyles.addButton]}
|
|
onPress={() => console.log("Add trip")}
|
|
activeOpacity={0.7}
|
|
>
|
|
<Ionicons name="add" size={20} color="#FFFFFF" />
|
|
<Text style={styles.addButtonText}>{t("diary.addTrip")}</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
|
|
{/* Trip Count */}
|
|
<Text style={[styles.countText, themedStyles.countText]}>
|
|
{t("diary.tripListCount", { count: tripsList?.total || 0 })}
|
|
</Text>
|
|
|
|
{/* Trip List */}
|
|
<ScrollView
|
|
style={styles.scrollView}
|
|
contentContainerStyle={styles.scrollContent}
|
|
showsVerticalScrollIndicator={false}
|
|
>
|
|
{tripsList?.trips?.map((trip) => (
|
|
<TripCard
|
|
key={trip.id}
|
|
trip={trip}
|
|
onPress={() => handleTripPress(trip.id)}
|
|
onView={() => handleViewTrip(trip.id)}
|
|
onEdit={() => handleEditTrip(trip.id)}
|
|
onTeam={() => handleViewTeam(trip.id)}
|
|
onSend={() => handleSendTrip(trip.id)}
|
|
onDelete={() => handleDeleteTrip(trip.id)}
|
|
/>
|
|
))}
|
|
|
|
{(!tripsList || !tripsList.trips || tripsList.trips.length === 0) && (
|
|
<View style={styles.emptyState}>
|
|
<Text style={[styles.emptyText, themedStyles.emptyText]}>
|
|
{t("diary.noTripsFound")}
|
|
</Text>
|
|
</View>
|
|
)}
|
|
</ScrollView>
|
|
</View>
|
|
|
|
{/* Filter Modal */}
|
|
<FilterModal
|
|
visible={showFilterModal}
|
|
onClose={() => setShowFilterModal(false)}
|
|
onApply={handleApplyFilters}
|
|
/>
|
|
</SafeAreaView>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
safeArea: {
|
|
flex: 1,
|
|
},
|
|
container: {
|
|
flex: 1,
|
|
padding: 10,
|
|
},
|
|
titleText: {
|
|
fontSize: 28,
|
|
fontWeight: "700",
|
|
lineHeight: 36,
|
|
marginBottom: 10,
|
|
fontFamily: Platform.select({
|
|
ios: "System",
|
|
android: "Roboto",
|
|
default: "System",
|
|
}),
|
|
},
|
|
actionRow: {
|
|
flexDirection: "row",
|
|
justifyContent: "flex-start",
|
|
alignItems: "center",
|
|
gap: 12,
|
|
marginBottom: 12,
|
|
},
|
|
headerRow: {
|
|
flexDirection: "row",
|
|
justifyContent: "space-between",
|
|
alignItems: "center",
|
|
marginTop: 20,
|
|
marginBottom: 12,
|
|
},
|
|
countText: {
|
|
fontSize: 16,
|
|
fontWeight: "600",
|
|
fontFamily: Platform.select({
|
|
ios: "System",
|
|
android: "Roboto",
|
|
default: "System",
|
|
}),
|
|
marginBottom: 10,
|
|
},
|
|
addButton: {
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
paddingHorizontal: 16,
|
|
paddingVertical: 8,
|
|
borderRadius: 8,
|
|
gap: 6,
|
|
},
|
|
addButtonText: {
|
|
fontSize: 14,
|
|
fontWeight: "600",
|
|
color: "#FFFFFF",
|
|
fontFamily: Platform.select({
|
|
ios: "System",
|
|
android: "Roboto",
|
|
default: "System",
|
|
}),
|
|
},
|
|
scrollView: {
|
|
flex: 1,
|
|
},
|
|
scrollContent: {
|
|
paddingBottom: 20,
|
|
},
|
|
emptyState: {
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
paddingVertical: 60,
|
|
},
|
|
emptyText: {
|
|
fontSize: 16,
|
|
fontFamily: Platform.select({
|
|
ios: "System",
|
|
android: "Roboto",
|
|
default: "System",
|
|
}),
|
|
},
|
|
});
|