From 0672f8adf90aca2d1b5f293c6c377c29efb08995 Mon Sep 17 00:00:00 2001 From: MinhNN Date: Thu, 4 Dec 2025 15:54:49 +0700 Subject: [PATCH] update interface, diary --- app/(tabs)/_layout.tsx | 2 +- app/(tabs)/diary.tsx | 67 +++++++++- app/(tabs)/index.tsx | 4 +- components/diary/StatusDropdown.tsx | 7 +- components/diary/TripCard.tsx | 183 ++++++++++++++++++++-------- components/diary/mockData.ts | 24 +++- components/diary/types.ts | 56 +++++---- components/ui/icon-symbol.tsx | 2 +- controller/typings.d.ts | 5 +- state/use-thing.ts | 31 +++++ 10 files changed, 291 insertions(+), 90 deletions(-) create mode 100644 state/use-thing.ts diff --git a/app/(tabs)/_layout.tsx b/app/(tabs)/_layout.tsx index bc60a1e..829e496 100644 --- a/app/(tabs)/_layout.tsx +++ b/app/(tabs)/_layout.tsx @@ -60,7 +60,7 @@ export default function TabLayout() { options={{ title: t("navigation.manager"), tabBarIcon: ({ color }) => ( - + ), }} /> diff --git a/app/(tabs)/diary.tsx b/app/(tabs)/diary.tsx index 0354918..e635ac8 100644 --- a/app/(tabs)/diary.tsx +++ b/app/(tabs)/diary.tsx @@ -1,5 +1,12 @@ -import { useState } from "react"; -import { Platform, ScrollView, StyleSheet, Text, TouchableOpacity, View } from "react-native"; +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 SearchBar from "@/components/diary/SearchBar"; @@ -7,6 +14,7 @@ import FilterButton from "@/components/diary/FilterButton"; import TripCard from "@/components/diary/TripCard"; import FilterModal, { FilterValues } from "@/components/diary/FilterModal"; import { MOCK_TRIPS } from "@/components/diary/mockData"; +import { useThings } from "@/state/use-thing"; export default function diary() { const [searchText, setSearchText] = useState(""); @@ -16,6 +24,23 @@ export default function diary() { startDate: null, endDate: null, }); + // Body 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 { things, getThings } = useThings(); + useEffect(() => { + getThings(payloadThings); + }, []); + + console.log(things); // Filter trips based on search text and filters const filteredTrips = MOCK_TRIPS.filter((trip) => { @@ -73,6 +98,31 @@ export default function diary() { 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 + }; + return ( @@ -80,7 +130,7 @@ export default function diary() { Nhật ký chuyến đi {/* Search Bar */} - + {/* Filter Button */} @@ -90,7 +140,7 @@ export default function diary() { Danh sách chuyến đi ({filteredTrips.length}) - console.log("Add trip")} activeOpacity={0.7} @@ -111,6 +161,11 @@ export default function diary() { 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)} /> ))} @@ -141,13 +196,13 @@ const styles = StyleSheet.create({ }, container: { flex: 1, - padding: 16, + padding: 10, }, titleText: { fontSize: 28, fontWeight: "700", lineHeight: 36, - marginBottom: 20, + marginBottom: 10, color: "#111827", fontFamily: Platform.select({ ios: "System", diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx index 9b030d8..baa2f07 100644 --- a/app/(tabs)/index.tsx +++ b/app/(tabs)/index.tsx @@ -73,7 +73,7 @@ export default function HomeScreen() { offset: 0, limit: 50, order: "name", - sort: "asc", + dir: "asc", metadata: { not_empty: "ship_id", }, @@ -384,7 +384,7 @@ export default function HomeScreen() { offset: 0, limit: 50, order: "name", - sort: "asc", + dir: "asc", metadata: { ...metaFormQuery, ...metaStateQuery, diff --git a/components/diary/StatusDropdown.tsx b/components/diary/StatusDropdown.tsx index a221324..55c1bdd 100644 --- a/components/diary/StatusDropdown.tsx +++ b/components/diary/StatusDropdown.tsx @@ -18,9 +18,11 @@ interface StatusDropdownProps { const STATUS_OPTIONS: Array<{ value: TripStatus | null; label: string }> = [ { value: null, label: "Vui lòng chọn" }, - { value: "completed", label: "Hoàn thành" }, + { value: "created", label: "Đã khởi tạo" }, + { value: "pending", label: "Chờ duyệt" }, + { value: "approved", label: "Đã duyệt" }, { value: "in-progress", label: "Đang hoạt động" }, - { value: "quality-check", label: "Đã khởi tạo" }, + { value: "completed", label: "Hoàn thành" }, { value: "cancelled", label: "Đã hủy" }, ]; @@ -162,7 +164,6 @@ const styles = StyleSheet.create({ paddingVertical: 16, borderBottomWidth: 1, borderBottomColor: "#F3F4F6", - }, selectedOption: { backgroundColor: "#EFF6FF", diff --git a/components/diary/TripCard.tsx b/components/diary/TripCard.tsx index bcfc60b..6df3c5f 100644 --- a/components/diary/TripCard.tsx +++ b/components/diary/TripCard.tsx @@ -12,72 +12,119 @@ import { Trip, TRIP_STATUS_CONFIG } from "./types"; interface TripCardProps { trip: Trip; onPress?: () => void; + onView?: () => void; + onEdit?: () => void; + onTeam?: () => void; + onSend?: () => void; + onDelete?: () => void; } -export default function TripCard({ trip, onPress }: TripCardProps) { +export default function TripCard({ trip, onPress, onView, onEdit, onTeam, onSend, onDelete }: TripCardProps) { const statusConfig = TRIP_STATUS_CONFIG[trip.status]; + // Determine which actions to show based on status + const showEdit = trip.status === 'created' || trip.status === 'pending'; + const showSend = trip.status === 'created'; + const showDelete = trip.status === 'pending'; + return ( - - {/* Header */} - - - - - {trip.title} - {trip.code} + + + {/* Header */} + + + + + {trip.title} + {trip.code} + - - - - {statusConfig.label} - + + {statusConfig.label} + + + + {/* Info Grid */} + + + Tàu + + {trip.vessel} ({trip.vesselCode}) + + + + + Khởi hành + {trip.departureDate} + + + + Trở về + {trip.returnDate || "-"} + + + + Thời gian + {trip.duration} + + + + + {/* Action Buttons */} + + + + + View + + + {showEdit && ( + + + Edit + + )} + + + + Team + + + {showSend && ( + + + Send + + )} + + {showDelete && ( + + + Delete + + )} - - {/* Info Grid */} - - - Tàu - - {trip.vessel} ({trip.vesselCode}) - - - - - Khởi hành - {trip.departureDate} - - - - Trở về - {trip.returnDate || "-"} - - - - Thời gian - {trip.duration} - - - + ); } @@ -178,4 +225,34 @@ const styles = StyleSheet.create({ duration: { color: "#3B82F6", }, + divider: { + height: 1, + backgroundColor: "#F3F4F6", + marginTop: 16, + }, + actionsContainer: { + flexDirection: "row", + justifyContent: "space-around", + paddingTop: 12, + }, + actionButton: { + flexDirection: "row", + alignItems: "center", + gap: 6, + paddingVertical: 8, + paddingHorizontal: 12, + }, + actionText: { + fontSize: 14, + color: "#6B7280", + fontWeight: "500", + fontFamily: Platform.select({ + ios: "System", + android: "Roboto", + default: "System", + }), + }, + deleteText: { + color: "#EF4444", + }, }); diff --git a/components/diary/mockData.ts b/components/diary/mockData.ts index fee5a59..9a2092f 100644 --- a/components/diary/mockData.ts +++ b/components/diary/mockData.ts @@ -54,7 +54,7 @@ export const MOCK_TRIPS: Trip[] = [ departureDate: "2025-11-18 07:00", returnDate: "2025-11-23 14:00", duration: "5 ngày 7 giờ", - status: "quality-check", + status: "created", }, { id: "T006", @@ -67,4 +67,26 @@ export const MOCK_TRIPS: Trip[] = [ duration: "6 giờ", status: "in-progress", }, + { + id: "T007", + title: "Khảo sát vùng biển", + code: "T007", + vessel: "Ngọc Lan", + vesselCode: "V002", + departureDate: "2025-12-01 07:00", + returnDate: null, + duration: "-", + status: "pending", + }, + { + id: "T008", + title: "Đánh cá xa bờ", + code: "T008", + vessel: "Việt Thắng", + vesselCode: "V003", + departureDate: "2025-12-05 05:00", + returnDate: null, + duration: "-", + status: "approved", + }, ]; diff --git a/components/diary/types.ts b/components/diary/types.ts index 8f09b3b..5177935 100644 --- a/components/diary/types.ts +++ b/components/diary/types.ts @@ -1,8 +1,10 @@ export type TripStatus = - | "completed" - | "in-progress" - | "cancelled" - | "quality-check"; + | "created" // Đã khởi tạo + | "pending" // Chờ duyệt + | "approved" // Đã duyệt + | "in-progress" // Đang hoạt động + | "completed" // Hoàn thành + | "cancelled"; // Đã hủy export interface Trip { id: string; @@ -17,28 +19,40 @@ export interface Trip { } export const TRIP_STATUS_CONFIG = { - completed: { - label: "Hoàn thành", - bgColor: "#D1FAE5", - textColor: "#065F46", - icon: "checkmark-circle", + created: { + label: "Đã khởi tạo", + bgColor: "#F3F4F6", // Gray background + textColor: "#4B5563", // Gray text + icon: "document-text", + }, + pending: { + label: "Chờ duyệt", + bgColor: "#FEF3C7", // Yellow background + textColor: "#92400E", // Dark yellow text + icon: "hourglass", + }, + approved: { + label: "Đã duyệt", + bgColor: "#E0E7FF", // Indigo background + textColor: "#3730A3", // Dark indigo text + icon: "checkmark-done", }, "in-progress": { - label: "Đang diễn ra", - bgColor: "#DBEAFE", - textColor: "#1E40AF", - icon: "time", + label: "Đang hoạt động", + bgColor: "#DBEAFE", // Blue background + textColor: "#1E40AF", // Dark blue text + icon: "sync", + }, + completed: { + label: "Hoàn thành", + bgColor: "#D1FAE5", // Green background + textColor: "#065F46", // Dark green text + icon: "checkmark-circle", }, cancelled: { label: "Đã hủy", - bgColor: "#FEE2E2", - textColor: "#991B1B", + bgColor: "#FEE2E2", // Red background + textColor: "#991B1B", // Dark red text icon: "close-circle", }, - "quality-check": { - label: "Khảo sát địa chất", - bgColor: "#D1FAE5", - textColor: "#065F46", - icon: "checkmark-circle", - }, } as const; diff --git a/components/ui/icon-symbol.tsx b/components/ui/icon-symbol.tsx index b6162bd..55b8fa8 100644 --- a/components/ui/icon-symbol.tsx +++ b/components/ui/icon-symbol.tsx @@ -31,7 +31,7 @@ const MAPPING = { xmark: "close", pencil: "edit", trash: "delete", - "square.stack.3d.up": "layers", + "square.stack.3d.up.fill": "layers", "bell.fill": "notifications", } as IconMapping; diff --git a/controller/typings.d.ts b/controller/typings.d.ts index f3f4942..e66f4a8 100644 --- a/controller/typings.d.ts +++ b/controller/typings.d.ts @@ -213,12 +213,13 @@ declare namespace Model { vn_law: boolean; } - // Seagateway Owner Appp + // Seagateway Owner App + // Thing interface SearchThingBody { offset?: number; limit?: number; order?: string; - sort?: "asc" | "desc"; + dir?: "asc" | "desc"; name?: string; metadata?: any; } diff --git a/state/use-thing.ts b/state/use-thing.ts new file mode 100644 index 0000000..61983b1 --- /dev/null +++ b/state/use-thing.ts @@ -0,0 +1,31 @@ +import { querySearchThings } from "@/controller/DeviceController"; +import { create } from "zustand"; + +type ThingState = { + things: Model.Thing[] | null; + getThings: (body: Model.SearchThingBody) => Promise; + error: string | null; + loading?: boolean; +}; + +export const useThings = create((set) => ({ + things: null, + getThings: async (body: Model.SearchThingBody) => { + set({ loading: true, error: null }); + try { + const response = await querySearchThings(body); + console.log("Things fetching API: ", response.data.things?.length); + + set({ things: response.data.things ?? [], loading: false }); + } catch (error) { + console.error("Error when fetch things: ", error); + set({ + error: "Failed to fetch things data", + loading: false, + things: null, + }); + } + }, + error: null, + loading: false, +}));