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,
+}));