thêm giao diện quản lý thuyền
This commit is contained in:
@@ -3,8 +3,10 @@ import { Tabs, useSegments } from "expo-router";
|
||||
import { HapticTab } from "@/components/haptic-tab";
|
||||
import { IconSymbol } from "@/components/ui/icon-symbol";
|
||||
import { Colors } from "@/constants/theme";
|
||||
import { queryProfile } from "@/controller/AuthController";
|
||||
import { useI18n } from "@/hooks/use-i18n";
|
||||
import { useColorScheme } from "@/hooks/use-theme-context";
|
||||
import { addUserStorage } from "@/utils/storage";
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
export default function TabLayout() {
|
||||
@@ -29,6 +31,23 @@ export default function TabLayout() {
|
||||
}
|
||||
}, [currentSegment]);
|
||||
|
||||
useEffect(() => {
|
||||
const getUserProfile = async () => {
|
||||
try {
|
||||
const resp = await queryProfile();
|
||||
if (resp.data && resp.status === 200) {
|
||||
await addUserStorage(
|
||||
resp.data.id || "",
|
||||
resp.data.metadata?.user_type || ""
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error when get Profile: ", error);
|
||||
}
|
||||
};
|
||||
getUserProfile();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Tabs
|
||||
screenOptions={{
|
||||
|
||||
@@ -1,35 +1,178 @@
|
||||
import { Platform, ScrollView, StyleSheet, Text, View } from "react-native";
|
||||
import DevicesScreen from "@/components/manager/devices";
|
||||
import FleetsScreen from "@/components/manager/fleets";
|
||||
import ShipsScreen from "@/components/manager/ships";
|
||||
import { ThemedText } from "@/components/themed-text";
|
||||
import { ThemedView } from "@/components/themed-view";
|
||||
import { Colors } from "@/config";
|
||||
import { ColorScheme, useTheme } from "@/hooks/use-theme-context";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { Animated, StyleSheet, TouchableOpacity, View } from "react-native";
|
||||
import { SafeAreaView } from "react-native-safe-area-context";
|
||||
|
||||
export default function manager() {
|
||||
const { colors, colorScheme } = useTheme();
|
||||
const styles = useMemo(
|
||||
() => createStyles(colors, colorScheme),
|
||||
[colors, colorScheme]
|
||||
);
|
||||
|
||||
const [selected, setSelected] = useState<"ships" | "devices" | "fleets">(
|
||||
"ships"
|
||||
);
|
||||
const [containerWidth, setContainerWidth] = useState(0);
|
||||
const indicatorTranslate = useRef(new Animated.Value(0)).current;
|
||||
const SEGMENT_COUNT = 3;
|
||||
const indexMap: Record<string, number> = {
|
||||
ships: 0,
|
||||
devices: 1,
|
||||
fleets: 2,
|
||||
};
|
||||
|
||||
const SegmentButton = ({
|
||||
label,
|
||||
active,
|
||||
onPress,
|
||||
}: {
|
||||
label: string;
|
||||
active?: boolean;
|
||||
onPress?: () => void;
|
||||
}) => {
|
||||
return (
|
||||
<TouchableOpacity
|
||||
style={[styles.segmentButton, active && styles.segmentButtonActive]}
|
||||
onPress={onPress}
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
<ThemedText
|
||||
style={[styles.segmentText, active && styles.segmentTextActive]}
|
||||
>
|
||||
{label}
|
||||
</ThemedText>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (containerWidth <= 0) return;
|
||||
const segmentWidth = containerWidth / SEGMENT_COUNT;
|
||||
const toValue = indexMap[selected] * segmentWidth;
|
||||
Animated.spring(indicatorTranslate, {
|
||||
toValue,
|
||||
useNativeDriver: true,
|
||||
friction: 14,
|
||||
tension: 100,
|
||||
}).start();
|
||||
}, [selected, containerWidth, indicatorTranslate]);
|
||||
return (
|
||||
<SafeAreaView style={{ flex: 1 }}>
|
||||
<ScrollView contentContainerStyle={styles.scrollContent}>
|
||||
<SafeAreaView style={{ flex: 1 }} edges={["top"]}>
|
||||
<ThemedView style={styles.scrollContent}>
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.titleText}>Quản lý tàu </Text>
|
||||
<ThemedView style={styles.header}>
|
||||
<View
|
||||
style={styles.segmentContainer}
|
||||
onLayout={(e) => setContainerWidth(e.nativeEvent.layout.width)}
|
||||
>
|
||||
{/* sliding indicator */}
|
||||
{containerWidth > 0 && (
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.indicator,
|
||||
{
|
||||
width: Math.max(containerWidth / SEGMENT_COUNT - 8, 0),
|
||||
transform: [{ translateX: indicatorTranslate }],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
|
||||
<SegmentButton
|
||||
label="Tàu"
|
||||
active={selected === "ships"}
|
||||
onPress={() => setSelected("ships")}
|
||||
/>
|
||||
<SegmentButton
|
||||
label="Thiết bị"
|
||||
active={selected === "devices"}
|
||||
onPress={() => setSelected("devices")}
|
||||
/>
|
||||
<SegmentButton
|
||||
label="Đội tàu"
|
||||
active={selected === "fleets"}
|
||||
onPress={() => setSelected("fleets")}
|
||||
/>
|
||||
</View>
|
||||
</ThemedView>
|
||||
|
||||
<View style={styles.contentWrapper}>
|
||||
{selected === "ships" && <ShipsScreen />}
|
||||
{selected === "devices" && <DevicesScreen />}
|
||||
{selected === "fleets" && <FleetsScreen />}
|
||||
</View>
|
||||
</View>
|
||||
</ScrollView>
|
||||
</ThemedView>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
scrollContent: {
|
||||
flexGrow: 1,
|
||||
},
|
||||
container: {
|
||||
alignItems: "center",
|
||||
padding: 15,
|
||||
},
|
||||
titleText: {
|
||||
fontSize: 32,
|
||||
fontWeight: "700",
|
||||
lineHeight: 40,
|
||||
marginBottom: 30,
|
||||
fontFamily: Platform.select({
|
||||
ios: "System",
|
||||
android: "Roboto",
|
||||
default: "System",
|
||||
}),
|
||||
},
|
||||
});
|
||||
const createStyles = (colors: typeof Colors.light, scheme: ColorScheme) =>
|
||||
StyleSheet.create({
|
||||
scrollContent: {
|
||||
flexGrow: 1,
|
||||
},
|
||||
container: {
|
||||
alignItems: "center",
|
||||
flex: 1,
|
||||
},
|
||||
|
||||
header: {
|
||||
width: "100%",
|
||||
paddingVertical: 8,
|
||||
paddingHorizontal: 4,
|
||||
},
|
||||
segmentContainer: {
|
||||
flexDirection: "row",
|
||||
backgroundColor: colors.backgroundSecondary,
|
||||
borderRadius: 10,
|
||||
padding: 4,
|
||||
alignSelf: "stretch",
|
||||
},
|
||||
segmentButton: {
|
||||
flex: 1,
|
||||
paddingVertical: 8,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
borderRadius: 8,
|
||||
},
|
||||
segmentButtonActive: {
|
||||
backgroundColor: scheme === "dark" ? "#435B66" : colors.surface,
|
||||
shadowColor: scheme === "dark" ? "transparent" : "#000",
|
||||
shadowOpacity: scheme === "dark" ? 0 : 0.1,
|
||||
shadowRadius: 4,
|
||||
elevation: scheme === "dark" ? 0 : 2,
|
||||
},
|
||||
segmentText: {
|
||||
fontSize: 14,
|
||||
fontWeight: "600",
|
||||
color: colors.textSecondary,
|
||||
},
|
||||
segmentTextActive: {
|
||||
color: colors.text,
|
||||
},
|
||||
indicator: {
|
||||
position: "absolute",
|
||||
left: 4,
|
||||
top: 4,
|
||||
bottom: 4,
|
||||
backgroundColor: scheme === "dark" ? "#435B66" : colors.surface,
|
||||
borderRadius: 8,
|
||||
shadowColor: scheme === "dark" ? "transparent" : "#000",
|
||||
shadowOpacity: scheme === "dark" ? 0 : 0.1,
|
||||
shadowRadius: 4,
|
||||
elevation: scheme === "dark" ? 0 : 2,
|
||||
},
|
||||
contentWrapper: {
|
||||
flex: 1,
|
||||
alignSelf: "stretch",
|
||||
width: "100%",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
// import ButtonCancelTrip from "@/components/ButtonCancelTrip";
|
||||
// import ButtonCreateNewHaulOrTrip from "@/components/ButtonCreateNewHaulOrTrip";
|
||||
// import ButtonEndTrip from "@/components/ButtonEndTrip";
|
||||
// import CrewListTable from "@/components/tripInfo/CrewListTable";
|
||||
// import FishingToolsTable from "@/components/tripInfo/FishingToolsList";
|
||||
// import NetListTable from "@/components/tripInfo/NetListTable";
|
||||
// import TripCostTable from "@/components/tripInfo/TripCostTable";
|
||||
// import { useI18n } from "@/hooks/use-i18n";
|
||||
// import { useThemeContext } from "@/hooks/use-theme-context";
|
||||
// import { Platform, ScrollView, StyleSheet, Text, View } from "react-native";
|
||||
// import { SafeAreaView } from "react-native-safe-area-context";
|
||||
|
||||
// export default function TripInfoScreen() {
|
||||
// const { t } = useI18n();
|
||||
// const { colors } = useThemeContext();
|
||||
// return (
|
||||
// <SafeAreaView style={styles.safeArea} edges={["top", "left", "right"]}>
|
||||
// <View style={styles.header}>
|
||||
// <Text style={[styles.titleText, { color: colors.text }]}>
|
||||
// {t("trip.infoTrip")}
|
||||
// </Text>
|
||||
// <View style={styles.buttonWrapper}>
|
||||
// <ButtonCreateNewHaulOrTrip />
|
||||
// </View>
|
||||
// </View>
|
||||
// <ScrollView contentContainerStyle={styles.scrollContent}>
|
||||
// <View style={styles.container}>
|
||||
// <TripCostTable />
|
||||
// <FishingToolsTable />
|
||||
// <CrewListTable />
|
||||
// <NetListTable />
|
||||
// <View style={styles.buttonRow}>
|
||||
// <ButtonCancelTrip />
|
||||
// <ButtonEndTrip />
|
||||
// </View>
|
||||
// </View>
|
||||
// </ScrollView>
|
||||
// </SafeAreaView>
|
||||
// );
|
||||
// }
|
||||
|
||||
// const styles = StyleSheet.create({
|
||||
// safeArea: {
|
||||
// flex: 1,
|
||||
// paddingBottom: 5,
|
||||
// },
|
||||
// scrollContent: {
|
||||
// flexGrow: 1,
|
||||
// },
|
||||
// header: {
|
||||
// width: "100%",
|
||||
// paddingHorizontal: 15,
|
||||
// paddingTop: 15,
|
||||
// paddingBottom: 10,
|
||||
// alignItems: "center",
|
||||
// },
|
||||
// buttonWrapper: {
|
||||
// width: "100%",
|
||||
// flexDirection: "row",
|
||||
// justifyContent: "flex-end",
|
||||
// },
|
||||
// container: {
|
||||
// alignItems: "center",
|
||||
// paddingHorizontal: 15,
|
||||
// },
|
||||
// buttonRow: {
|
||||
// flexDirection: "row",
|
||||
// gap: 10,
|
||||
// marginTop: 15,
|
||||
// marginBottom: 15,
|
||||
// },
|
||||
// titleText: {
|
||||
// fontSize: 32,
|
||||
// fontWeight: "700",
|
||||
// lineHeight: 40,
|
||||
// paddingBottom: 10,
|
||||
// fontFamily: Platform.select({
|
||||
// ios: "System",
|
||||
// android: "Roboto",
|
||||
// default: "System",
|
||||
// }),
|
||||
// },
|
||||
// });
|
||||
Reference in New Issue
Block a user