Unifies naming conventions for users, companies, and rides across backend and mobile app, standardizing field names for consistency and easier data interchange. Introduces endpoints to fetch all companies and rides for sync. Enhances sync logic to support both upload and download of companies, including first-time population from server. Updates local database schema and access logic to match backend structure, improving maintainability and reliability of sync.
177 lines
4.3 KiB
TypeScript
177 lines
4.3 KiB
TypeScript
import { BORDER_RADIUS, COLORS, SPACING } from "@/constants/theme";
|
|
import React, { useState } from "react";
|
|
import {
|
|
FlatList,
|
|
Modal,
|
|
StyleSheet,
|
|
Text,
|
|
TouchableOpacity,
|
|
View,
|
|
} from "react-native";
|
|
import { SafeAreaView } from "react-native-safe-area-context";
|
|
|
|
type SelectProps = {
|
|
label?: string;
|
|
value: string;
|
|
onValueChange: (value: string) => void;
|
|
items: { label: string; value: string }[];
|
|
};
|
|
|
|
export function Select({ label, value, onValueChange, items }: SelectProps) {
|
|
const [open, setOpen] = useState(false);
|
|
|
|
const selected = items.find((i) => i.value === value);
|
|
|
|
return (
|
|
<View style={styles.container}>
|
|
{label && <Text style={styles.label}>{label}</Text>}
|
|
|
|
<TouchableOpacity
|
|
style={styles.trigger}
|
|
onPress={() => setOpen(true)}
|
|
activeOpacity={0.7}
|
|
>
|
|
<Text
|
|
style={[
|
|
styles.triggerText,
|
|
!selected?.value && styles.triggerPlaceholder,
|
|
]}
|
|
numberOfLines={1}
|
|
>
|
|
{selected?.label ?? "Selecione"}
|
|
</Text>
|
|
<Text style={styles.arrow}>▾</Text>
|
|
</TouchableOpacity>
|
|
|
|
<Modal visible={open} transparent animationType="fade">
|
|
<TouchableOpacity
|
|
style={styles.backdrop}
|
|
activeOpacity={1}
|
|
onPress={() => setOpen(false)}
|
|
>
|
|
<SafeAreaView style={styles.sheet} onStartShouldSetResponder={() => true}>
|
|
<View style={styles.sheetHandle} />
|
|
<FlatList
|
|
data={items}
|
|
keyExtractor={(item) => item.value}
|
|
renderItem={({ item }) => (
|
|
<TouchableOpacity
|
|
style={[
|
|
styles.option,
|
|
item.value === value && styles.optionSelected,
|
|
]}
|
|
onPress={() => {
|
|
onValueChange(item.value);
|
|
setOpen(false);
|
|
}}
|
|
activeOpacity={0.7}
|
|
>
|
|
<Text
|
|
style={[
|
|
styles.optionText,
|
|
item.value === value && styles.optionTextSelected,
|
|
!item.value && styles.optionPlaceholder,
|
|
]}
|
|
>
|
|
{item.label}
|
|
</Text>
|
|
{item.value === value && (
|
|
<Text style={styles.checkmark}>✓</Text>
|
|
)}
|
|
</TouchableOpacity>
|
|
)}
|
|
/>
|
|
</SafeAreaView>
|
|
</TouchableOpacity>
|
|
</Modal>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
width: "100%",
|
|
},
|
|
label: {
|
|
color: COLORS.text,
|
|
fontSize: 14,
|
|
fontWeight: "600",
|
|
marginBottom: SPACING.sm,
|
|
},
|
|
trigger: {
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
justifyContent: "space-between",
|
|
height: 48,
|
|
borderWidth: 1,
|
|
borderColor: COLORS.inputBorder,
|
|
backgroundColor: COLORS.inputBackground,
|
|
borderRadius: BORDER_RADIUS.md,
|
|
paddingHorizontal: SPACING.md,
|
|
},
|
|
triggerText: {
|
|
flex: 1,
|
|
fontSize: 15,
|
|
color: COLORS.inputText,
|
|
},
|
|
triggerPlaceholder: {
|
|
color: COLORS.inputPlaceholder,
|
|
},
|
|
arrow: {
|
|
fontSize: 14,
|
|
color: COLORS.textSecondary,
|
|
marginLeft: SPACING.sm,
|
|
},
|
|
|
|
// Modal
|
|
backdrop: {
|
|
flex: 1,
|
|
backgroundColor: "rgba(0,0,0,0.6)",
|
|
justifyContent: "flex-end",
|
|
},
|
|
sheet: {
|
|
backgroundColor: COLORS.surface,
|
|
borderTopLeftRadius: BORDER_RADIUS.xl,
|
|
borderTopRightRadius: BORDER_RADIUS.xl,
|
|
maxHeight: "60%",
|
|
paddingBottom: SPACING.lg,
|
|
},
|
|
sheetHandle: {
|
|
width: 40,
|
|
height: 4,
|
|
backgroundColor: COLORS.border,
|
|
borderRadius: 2,
|
|
alignSelf: "center",
|
|
marginTop: SPACING.md,
|
|
marginBottom: SPACING.sm,
|
|
},
|
|
option: {
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
justifyContent: "space-between",
|
|
paddingVertical: SPACING.md,
|
|
paddingHorizontal: SPACING.lg,
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: COLORS.border,
|
|
},
|
|
optionSelected: {
|
|
backgroundColor: COLORS.surfaceLight,
|
|
},
|
|
optionText: {
|
|
fontSize: 15,
|
|
color: COLORS.text,
|
|
flex: 1,
|
|
},
|
|
optionTextSelected: {
|
|
fontWeight: "700",
|
|
color: COLORS.success,
|
|
},
|
|
optionPlaceholder: {
|
|
color: COLORS.textTertiary,
|
|
},
|
|
checkmark: {
|
|
fontSize: 16,
|
|
color: COLORS.success,
|
|
fontWeight: "700",
|
|
},
|
|
});
|