thêm tab "Xem chi tiết chuyến đi", "Xem chi tiết thành viên chuyến đi", tái sử dụng lại components modal tripForm

This commit is contained in:
2025-12-23 23:10:19 +07:00
parent afc6acbfe2
commit 000a4ed856
22 changed files with 3221 additions and 379 deletions

View File

@@ -0,0 +1,127 @@
import React from "react";
import {
View,
Text,
StyleSheet,
Platform,
TouchableOpacity,
} from "react-native";
import { Ionicons } from "@expo/vector-icons";
import { useThemeContext } from "@/hooks/use-theme-context";
interface SectionCardProps {
title: string;
icon: keyof typeof Ionicons.glyphMap;
children: React.ReactNode;
count?: number;
collapsible?: boolean;
defaultExpanded?: boolean;
}
export default function SectionCard({
title,
icon,
children,
count,
collapsible = false,
defaultExpanded = true,
}: SectionCardProps) {
const { colors } = useThemeContext();
const [expanded, setExpanded] = React.useState(defaultExpanded);
const themedStyles = {
container: {
backgroundColor: colors.card,
borderColor: colors.separator,
},
title: {
color: colors.text,
},
count: {
color: colors.textSecondary,
backgroundColor: colors.backgroundSecondary,
},
icon: colors.primary,
};
return (
<View style={[styles.container, themedStyles.container]}>
<TouchableOpacity
style={styles.header}
onPress={() => collapsible && setExpanded(!expanded)}
activeOpacity={collapsible ? 0.7 : 1}
disabled={!collapsible}
>
<View style={styles.headerLeft}>
<Ionicons name={icon} size={20} color={themedStyles.icon} />
<Text style={[styles.title, themedStyles.title]}>{title}</Text>
{count !== undefined && (
<View style={[styles.countBadge, themedStyles.count]}>
<Text style={[styles.countText, { color: colors.textSecondary }]}>
{count}
</Text>
</View>
)}
</View>
{collapsible && (
<Ionicons
name={expanded ? "chevron-up" : "chevron-down"}
size={20}
color={colors.textSecondary}
/>
)}
</TouchableOpacity>
{expanded && <View style={styles.content}>{children}</View>}
</View>
);
}
const styles = StyleSheet.create({
container: {
borderRadius: 12,
borderWidth: 1,
marginBottom: 16,
overflow: "hidden",
},
header: {
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
paddingHorizontal: 16,
paddingVertical: 14,
},
headerLeft: {
flexDirection: "row",
alignItems: "center",
gap: 10,
},
title: {
fontSize: 16,
fontWeight: "600",
fontFamily: Platform.select({
ios: "System",
android: "Roboto",
default: "System",
}),
},
countBadge: {
paddingHorizontal: 8,
paddingVertical: 2,
borderRadius: 10,
minWidth: 24,
alignItems: "center",
},
countText: {
fontSize: 12,
fontWeight: "600",
fontFamily: Platform.select({
ios: "System",
android: "Roboto",
default: "System",
}),
},
content: {
paddingHorizontal: 16,
paddingBottom: 16,
},
});