top-tran/toptran-app/src/app/historico.tsx

309 lines
7.8 KiB
TypeScript
Raw Normal View History

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.ride_date,
empresa: c.company,
2026-05-03 02:47:00 -03:00
km: c.km,
custoPorKm: c.cost_per_km,
2026-05-03 02:47:00 -03:00
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 -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
},
});