2026-05-03 02:47:00 -03:00
|
|
|
import { BORDER_RADIUS, COLORS, SPACING } from "@/constants/theme";
|
|
|
|
|
import { useAuth } from "@/contexts/AuthContext";
|
|
|
|
|
import { obterCorridas } from "@/services/db";
|
|
|
|
|
import { router, useFocusEffect } from "expo-router";
|
|
|
|
|
import React, { useCallback, useState } from "react";
|
|
|
|
|
import {
|
|
|
|
|
FlatList,
|
|
|
|
|
ScrollView,
|
|
|
|
|
StyleSheet,
|
|
|
|
|
Text,
|
|
|
|
|
TouchableOpacity,
|
|
|
|
|
View,
|
|
|
|
|
} from "react-native";
|
2026-05-03 01:16:25 -03:00
|
|
|
import { SafeAreaView } from "react-native-safe-area-context";
|
|
|
|
|
|
2026-05-03 02:47:00 -03:00
|
|
|
type Corrida = {
|
2026-05-03 01:16:25 -03:00
|
|
|
id: string;
|
|
|
|
|
data: string;
|
|
|
|
|
empresa: string;
|
|
|
|
|
km: number;
|
|
|
|
|
custoPorKm: number;
|
|
|
|
|
total: number;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const HistoricoItem = ({ item, index }: { item: Corrida; index: number }) => (
|
|
|
|
|
<View style={styles.item}>
|
|
|
|
|
<View style={styles.itemHeader}>
|
|
|
|
|
<Text style={styles.itemNumber}>#{index + 1}</Text>
|
|
|
|
|
<Text style={styles.itemDate}>{item.data}</Text>
|
|
|
|
|
</View>
|
|
|
|
|
|
|
|
|
|
<View style={styles.itemEmpresa}>
|
|
|
|
|
<Text style={styles.empresaLabel}>{item.empresa}</Text>
|
|
|
|
|
</View>
|
|
|
|
|
|
|
|
|
|
<View style={styles.itemDetails}>
|
|
|
|
|
<View style={styles.detailBox}>
|
|
|
|
|
<Text style={styles.detailLabel}>Distância</Text>
|
|
|
|
|
<Text style={styles.detailValue}>{item.km} km</Text>
|
|
|
|
|
</View>
|
|
|
|
|
<View style={styles.detailBox}>
|
|
|
|
|
<Text style={styles.detailLabel}>Valor/km</Text>
|
|
|
|
|
<Text style={styles.detailValue}>R$ {item.custoPorKm.toFixed(2)}</Text>
|
|
|
|
|
</View>
|
2026-05-03 02:47:00 -03:00
|
|
|
<View style={[styles.detailBox, styles.detailBoxHighlight]}>
|
2026-05-03 01:16:25 -03:00
|
|
|
<Text style={styles.detailLabel}>Total</Text>
|
2026-05-03 02:47:00 -03:00
|
|
|
<Text style={[styles.detailValue, styles.detailValueHighlight]}>
|
|
|
|
|
R$ {item.total.toFixed(2)}
|
|
|
|
|
</Text>
|
2026-05-03 01:16:25 -03:00
|
|
|
</View>
|
|
|
|
|
</View>
|
|
|
|
|
</View>
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-03 02:47:00 -03:00
|
|
|
export default function HistoricoPage() {
|
|
|
|
|
const { user } = useAuth();
|
|
|
|
|
const [corridas, setCorridas] = useState<Corrida[]>([]);
|
|
|
|
|
|
|
|
|
|
const loadCorridas = useCallback(async () => {
|
|
|
|
|
if (!user?.id) return;
|
|
|
|
|
try {
|
|
|
|
|
const data = await obterCorridas(user.id);
|
|
|
|
|
setCorridas(
|
|
|
|
|
data.map((c) => ({
|
|
|
|
|
id: c.id,
|
|
|
|
|
data: c.data,
|
|
|
|
|
empresa: c.empresa,
|
|
|
|
|
km: c.km,
|
|
|
|
|
custoPorKm: c.custo_por_km,
|
|
|
|
|
total: c.total,
|
|
|
|
|
})),
|
|
|
|
|
);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("Erro ao carregar histórico:", error);
|
|
|
|
|
}
|
|
|
|
|
}, [user?.id]);
|
|
|
|
|
|
|
|
|
|
useFocusEffect(useCallback(() => { loadCorridas(); }, [loadCorridas]));
|
|
|
|
|
|
|
|
|
|
const totalGanhos = corridas.reduce((acc, c) => acc + c.total, 0);
|
|
|
|
|
const totalKm = corridas.reduce((acc, c) => acc + c.km, 0);
|
|
|
|
|
|
2026-05-03 01:16:25 -03:00
|
|
|
return (
|
|
|
|
|
<SafeAreaView style={styles.container}>
|
|
|
|
|
<View style={styles.header}>
|
2026-05-03 02:47:00 -03:00
|
|
|
<TouchableOpacity onPress={() => router.back()} style={styles.backButton} activeOpacity={0.7}>
|
|
|
|
|
<Text style={styles.backIcon}>←</Text>
|
|
|
|
|
<Text style={styles.backLabel}>Voltar</Text>
|
|
|
|
|
</TouchableOpacity>
|
|
|
|
|
</View>
|
|
|
|
|
|
|
|
|
|
<View style={styles.titleSection}>
|
|
|
|
|
<Text style={styles.title}>Histórico</Text>
|
2026-05-03 01:16:25 -03:00
|
|
|
<Text style={styles.subtitle}>
|
2026-05-03 02:47:00 -03:00
|
|
|
{corridas.length} corrida{corridas.length !== 1 ? "s" : ""} registrada{corridas.length !== 1 ? "s" : ""}
|
2026-05-03 01:16:25 -03:00
|
|
|
</Text>
|
|
|
|
|
</View>
|
|
|
|
|
|
2026-05-03 02:47:00 -03:00
|
|
|
{corridas.length > 0 && (
|
|
|
|
|
<View style={styles.summaryRow}>
|
|
|
|
|
<View style={styles.summaryCard}>
|
|
|
|
|
<Text style={styles.summaryValue}>{totalKm.toFixed(0)} km</Text>
|
|
|
|
|
<Text style={styles.summaryLabel}>Total Rodado</Text>
|
|
|
|
|
</View>
|
|
|
|
|
<View style={[styles.summaryCard, styles.summaryCardAccent]}>
|
|
|
|
|
<Text style={[styles.summaryValue, styles.summaryValueAccent]}>
|
|
|
|
|
R$ {totalGanhos.toFixed(2)}
|
|
|
|
|
</Text>
|
|
|
|
|
<Text style={styles.summaryLabel}>Total Ganho</Text>
|
|
|
|
|
</View>
|
|
|
|
|
</View>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{corridas.length === 0 ? (
|
2026-05-03 01:16:25 -03:00
|
|
|
<ScrollView contentContainerStyle={styles.emptyContainer}>
|
|
|
|
|
<View style={styles.emptyBox}>
|
|
|
|
|
<Text style={styles.emptyEmoji}>🚗</Text>
|
2026-05-03 02:47:00 -03:00
|
|
|
<Text style={styles.emptyTitle}>Nenhuma corrida ainda</Text>
|
|
|
|
|
<Text style={styles.emptyText}>
|
|
|
|
|
Registre sua primeira corrida para vê-la aqui.
|
|
|
|
|
</Text>
|
2026-05-03 01:16:25 -03:00
|
|
|
</View>
|
|
|
|
|
</ScrollView>
|
|
|
|
|
) : (
|
|
|
|
|
<FlatList
|
2026-05-03 02:47:00 -03:00
|
|
|
data={corridas}
|
2026-05-03 01:16:25 -03:00
|
|
|
keyExtractor={(item) => item.id}
|
|
|
|
|
renderItem={({ item, index }) => (
|
|
|
|
|
<HistoricoItem item={item} index={index} />
|
|
|
|
|
)}
|
|
|
|
|
contentContainerStyle={styles.listContent}
|
|
|
|
|
showsVerticalScrollIndicator={false}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
</SafeAreaView>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const styles = StyleSheet.create({
|
|
|
|
|
container: {
|
|
|
|
|
flex: 1,
|
2026-05-03 02:47:00 -03:00
|
|
|
backgroundColor: COLORS.background,
|
2026-05-03 01:16:25 -03:00
|
|
|
},
|
|
|
|
|
header: {
|
2026-05-03 02:47:00 -03:00
|
|
|
paddingHorizontal: SPACING.lg,
|
|
|
|
|
paddingVertical: SPACING.md,
|
|
|
|
|
borderBottomWidth: 1,
|
|
|
|
|
borderBottomColor: COLORS.border,
|
|
|
|
|
},
|
|
|
|
|
backButton: {
|
|
|
|
|
flexDirection: "row",
|
|
|
|
|
alignItems: "center",
|
|
|
|
|
gap: SPACING.xs,
|
|
|
|
|
alignSelf: "flex-start",
|
|
|
|
|
},
|
|
|
|
|
backIcon: {
|
|
|
|
|
fontSize: 18,
|
|
|
|
|
color: COLORS.text,
|
|
|
|
|
},
|
|
|
|
|
backLabel: {
|
|
|
|
|
fontSize: 15,
|
|
|
|
|
color: COLORS.textSecondary,
|
|
|
|
|
fontWeight: "500",
|
|
|
|
|
},
|
|
|
|
|
titleSection: {
|
|
|
|
|
paddingHorizontal: SPACING.lg,
|
|
|
|
|
paddingTop: SPACING.lg,
|
|
|
|
|
paddingBottom: SPACING.md,
|
2026-05-03 01:16:25 -03:00
|
|
|
},
|
|
|
|
|
title: {
|
|
|
|
|
fontSize: 28,
|
|
|
|
|
fontWeight: "800",
|
2026-05-03 02:47:00 -03:00
|
|
|
color: COLORS.text,
|
2026-05-03 01:16:25 -03:00
|
|
|
marginBottom: 4,
|
|
|
|
|
},
|
|
|
|
|
subtitle: {
|
2026-05-03 02:47:00 -03:00
|
|
|
fontSize: 13,
|
|
|
|
|
color: COLORS.textTertiary,
|
|
|
|
|
},
|
|
|
|
|
summaryRow: {
|
|
|
|
|
flexDirection: "row",
|
|
|
|
|
gap: SPACING.sm,
|
|
|
|
|
paddingHorizontal: SPACING.lg,
|
|
|
|
|
paddingBottom: SPACING.lg,
|
|
|
|
|
},
|
|
|
|
|
summaryCard: {
|
|
|
|
|
flex: 1,
|
|
|
|
|
backgroundColor: COLORS.surface,
|
|
|
|
|
borderRadius: BORDER_RADIUS.lg,
|
|
|
|
|
padding: SPACING.md,
|
|
|
|
|
borderWidth: 1,
|
|
|
|
|
borderColor: COLORS.border,
|
|
|
|
|
},
|
|
|
|
|
summaryCardAccent: {
|
|
|
|
|
borderColor: COLORS.success,
|
|
|
|
|
},
|
|
|
|
|
summaryValue: {
|
|
|
|
|
fontSize: 18,
|
|
|
|
|
fontWeight: "800",
|
|
|
|
|
color: COLORS.text,
|
|
|
|
|
marginBottom: 2,
|
|
|
|
|
},
|
|
|
|
|
summaryValueAccent: {
|
|
|
|
|
color: COLORS.success,
|
|
|
|
|
},
|
|
|
|
|
summaryLabel: {
|
|
|
|
|
fontSize: 11,
|
|
|
|
|
color: COLORS.textTertiary,
|
|
|
|
|
fontWeight: "600",
|
|
|
|
|
textTransform: "uppercase",
|
|
|
|
|
letterSpacing: 0.5,
|
2026-05-03 01:16:25 -03:00
|
|
|
},
|
|
|
|
|
listContent: {
|
2026-05-03 02:47:00 -03:00
|
|
|
padding: SPACING.lg,
|
|
|
|
|
paddingBottom: SPACING.xxl,
|
2026-05-03 01:16:25 -03:00
|
|
|
},
|
|
|
|
|
item: {
|
2026-05-03 02:47:00 -03:00
|
|
|
backgroundColor: COLORS.surface,
|
|
|
|
|
borderRadius: BORDER_RADIUS.lg,
|
|
|
|
|
marginBottom: SPACING.md,
|
2026-05-03 01:16:25 -03:00
|
|
|
overflow: "hidden",
|
|
|
|
|
borderWidth: 1,
|
2026-05-03 02:47:00 -03:00
|
|
|
borderColor: COLORS.border,
|
2026-05-03 01:16:25 -03:00
|
|
|
},
|
|
|
|
|
itemHeader: {
|
|
|
|
|
flexDirection: "row",
|
2026-05-03 02:47:00 -03:00
|
|
|
paddingHorizontal: SPACING.lg,
|
|
|
|
|
paddingTop: SPACING.md,
|
|
|
|
|
paddingBottom: SPACING.sm,
|
2026-05-03 01:16:25 -03:00
|
|
|
justifyContent: "space-between",
|
|
|
|
|
alignItems: "center",
|
|
|
|
|
borderBottomWidth: 1,
|
2026-05-03 02:47:00 -03:00
|
|
|
borderBottomColor: COLORS.border,
|
2026-05-03 01:16:25 -03:00
|
|
|
},
|
|
|
|
|
itemNumber: {
|
|
|
|
|
fontSize: 12,
|
|
|
|
|
fontWeight: "700",
|
2026-05-03 02:47:00 -03:00
|
|
|
color: COLORS.textTertiary,
|
|
|
|
|
letterSpacing: 0.5,
|
2026-05-03 01:16:25 -03:00
|
|
|
},
|
|
|
|
|
itemDate: {
|
|
|
|
|
fontSize: 12,
|
2026-05-03 02:47:00 -03:00
|
|
|
color: COLORS.textTertiary,
|
2026-05-03 01:16:25 -03:00
|
|
|
},
|
|
|
|
|
itemEmpresa: {
|
2026-05-03 02:47:00 -03:00
|
|
|
paddingHorizontal: SPACING.lg,
|
|
|
|
|
paddingVertical: SPACING.sm,
|
|
|
|
|
backgroundColor: COLORS.surfaceLight,
|
2026-05-03 01:16:25 -03:00
|
|
|
},
|
|
|
|
|
empresaLabel: {
|
|
|
|
|
fontSize: 14,
|
2026-05-03 02:47:00 -03:00
|
|
|
fontWeight: "700",
|
|
|
|
|
color: COLORS.text,
|
2026-05-03 01:16:25 -03:00
|
|
|
},
|
|
|
|
|
itemDetails: {
|
|
|
|
|
flexDirection: "row",
|
2026-05-03 02:47:00 -03:00
|
|
|
paddingHorizontal: SPACING.lg,
|
|
|
|
|
paddingVertical: SPACING.md,
|
2026-05-03 01:16:25 -03:00
|
|
|
},
|
|
|
|
|
detailBox: {
|
|
|
|
|
flex: 1,
|
2026-05-03 02:47:00 -03:00
|
|
|
},
|
|
|
|
|
detailBoxHighlight: {
|
|
|
|
|
alignItems: "flex-end",
|
2026-05-03 01:16:25 -03:00
|
|
|
},
|
|
|
|
|
detailLabel: {
|
2026-05-03 02:47:00 -03:00
|
|
|
fontSize: 10,
|
|
|
|
|
color: COLORS.textTertiary,
|
|
|
|
|
fontWeight: "600",
|
|
|
|
|
textTransform: "uppercase",
|
|
|
|
|
letterSpacing: 0.4,
|
|
|
|
|
marginBottom: 3,
|
2026-05-03 01:16:25 -03:00
|
|
|
},
|
|
|
|
|
detailValue: {
|
|
|
|
|
fontSize: 14,
|
|
|
|
|
fontWeight: "700",
|
2026-05-03 02:47:00 -03:00
|
|
|
color: COLORS.text,
|
|
|
|
|
},
|
|
|
|
|
detailValueHighlight: {
|
|
|
|
|
color: COLORS.success,
|
|
|
|
|
fontSize: 16,
|
2026-05-03 01:16:25 -03:00
|
|
|
},
|
|
|
|
|
emptyContainer: {
|
|
|
|
|
flex: 1,
|
|
|
|
|
justifyContent: "center",
|
|
|
|
|
alignItems: "center",
|
2026-05-03 02:47:00 -03:00
|
|
|
padding: SPACING.lg,
|
2026-05-03 01:16:25 -03:00
|
|
|
},
|
|
|
|
|
emptyBox: {
|
|
|
|
|
alignItems: "center",
|
|
|
|
|
},
|
|
|
|
|
emptyEmoji: {
|
|
|
|
|
fontSize: 48,
|
2026-05-03 02:47:00 -03:00
|
|
|
marginBottom: SPACING.lg,
|
2026-05-03 01:16:25 -03:00
|
|
|
},
|
|
|
|
|
emptyTitle: {
|
|
|
|
|
fontSize: 18,
|
2026-05-03 02:47:00 -03:00
|
|
|
fontWeight: "700",
|
|
|
|
|
color: COLORS.text,
|
|
|
|
|
marginBottom: SPACING.sm,
|
2026-05-03 01:16:25 -03:00
|
|
|
},
|
|
|
|
|
emptyText: {
|
|
|
|
|
fontSize: 14,
|
2026-05-03 02:47:00 -03:00
|
|
|
color: COLORS.textTertiary,
|
2026-05-03 01:16:25 -03:00
|
|
|
textAlign: "center",
|
2026-05-03 02:47:00 -03:00
|
|
|
lineHeight: 20,
|
2026-05-03 01:16:25 -03:00
|
|
|
},
|
|
|
|
|
});
|