import React, { useEffect } from 'react'; import { View, Text, ScrollView, TouchableOpacity, StyleSheet, } from 'react-native'; import { useRouter } from 'expo-router'; import { Ionicons } from '@expo/vector-icons'; import { SafeAreaView } from 'react-native-safe-area-context'; import { useFilamentStore } from '@store/filamentStore'; import { useAuthStore } from '@store/authStore'; import { ColorSwatch } from '@presentation/components/filament/ColorSwatch'; import { StockBar, StockBadge } from '@presentation/components/filament/StockBar'; import { Card } from '@presentation/components/ui/Card'; import { calcFilamentPercentage } from '@domain/Filament'; import { formatWeight } from '@shared/utils/filament'; import { colors, typography, spacing, radius } from '@shared/theme'; /** * Tela Home / Dashboard — 1-0 * * Seções: * - Totais (estoque total em kg, rolos acabando) * - Uso Recente (2 últimos filamentos, cards grandes) * - Inventário rápido (lista com botão "Pesar") * - Por Material (resumo agregado) * - Estoque Baixo (filamentos ≤35%) */ export default function HomeScreen(): React.ReactElement { const router = useRouter(); const { user } = useAuthStore(); const { filaments, isLoading } = useFilamentStore(); // Dados derivados const totalKg = (filaments.reduce((s, f) => s + f.netWeightG, 0) / 1000).toFixed(1); const lowStockFilaments = filaments.filter((f) => calcFilamentPercentage(f) <= 35); const recentFilaments = [...filaments].sort( (a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime(), ).slice(0, 2); // Agrupamento por material const byMaterial = filaments.reduce>((acc, f) => { if (!acc[f.material]) acc[f.material] = { count: 0, totalG: 0 }; acc[f.material].count += 1; acc[f.material].totalG += f.netWeightG; return acc; }, {}); return ( {/* Cabeçalho */} Bem-vindo de volta, Seu Inventário router.push('/(app)/scanner' as never)} > {/* Totais */} Total em Estoque {totalKg} kg Rolos Acabando {lowStockFilaments.length} unidades {/* Uso Recente */} {recentFilaments.length > 0 && ( <> USO RECENTE {recentFilaments.map((f) => { const pct = calcFilamentPercentage(f); return ( router.push(`/(app)/inventory/${f.id}`)} activeOpacity={0.8} > {f.model ?? f.material} {f.brand} · {f.colorHex} {formatWeight(f.netWeightG)} ); })} )} {/* Inventário rápido */} {filaments.length > 0 && ( <> INVENTÁRIO router.push('/(app)/(tabs)/inventory')}> Ver todos {filaments.slice(0, 4).map((f) => ( router.push(`/(app)/inventory/${f.id}`)} activeOpacity={0.8} > {f.model ?? f.material} {f.tempHotendC ? `${f.tempHotendC}°C` : '—'} / {f.tempBedC ? `${f.tempBedC}°C` : '—'} · Fluxo {f.flowFactorPct ?? '—'} {formatWeight(f.netWeightG)} Pesar ))} )} {/* Por Material */} {Object.keys(byMaterial).length > 0 && ( <> POR MATERIAL {Object.entries(byMaterial).map(([mat, data]) => ( {mat} {data.count} rolos {(data.totalG / 1000).toFixed(1)} kg ))} )} {/* Estoque Baixo */} {lowStockFilaments.length > 0 && ( <> ESTOQUE BAIXO {lowStockFilaments.map((f) => { const pct = calcFilamentPercentage(f); return ( router.push(`/(app)/inventory/${f.id}`)} activeOpacity={0.8} > {f.model ?? f.material} {f.brand} · {f.material} {formatWeight(f.netWeightG)} ); })} )} {/* Estado vazio */} {!isLoading && filaments.length === 0 && ( Nenhum filamento ainda Toque em + para adicionar seu primeiro filamento. )} ); } const styles = StyleSheet.create({ safe: { flex: 1, backgroundColor: colors.bgBase }, scroll: { padding: spacing[5], gap: spacing[4], paddingBottom: spacing[10] }, headerRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: spacing[2] }, welcomeText: { fontFamily: typography.fontFamily.ui, fontSize: typography.fontSize.sm, color: colors.textSecondary }, title: { fontFamily: typography.fontFamily.ui, fontSize: typography.fontSize['2xl'], fontWeight: typography.fontWeight.bold, color: colors.textPrimary }, qrBtn: { width: 40, height: 40, borderRadius: radius.md, backgroundColor: colors.bgSurface, alignItems: 'center', justifyContent: 'center', borderWidth: 1, borderColor: colors.border }, statsRow: { flexDirection: 'row', gap: spacing[3] }, statCard: { flex: 1, gap: spacing[1] }, statLabel: { fontFamily: typography.fontFamily.ui, fontSize: typography.fontSize.xs, color: colors.textSecondary }, statValue: { fontFamily: typography.fontFamily.ui, fontSize: typography.fontSize.xl, fontWeight: typography.fontWeight.bold, color: colors.accent }, statValueAlert: { fontFamily: typography.fontFamily.ui, fontSize: typography.fontSize.xl, fontWeight: typography.fontWeight.bold, color: colors.textPrimary }, sectionTitle: { fontFamily: typography.fontFamily.ui, fontSize: typography.fontSize.xs, fontWeight: typography.fontWeight.bold, color: colors.textSecondary, letterSpacing: 0.8, textTransform: 'uppercase', marginTop: spacing[2] }, sectionHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginTop: spacing[2] }, sectionLink: { fontFamily: typography.fontFamily.ui, fontSize: typography.fontSize.sm, color: colors.accent }, recentScroll: { marginHorizontal: -spacing[5] }, recentCard: { width: 160, backgroundColor: colors.bgSurface, borderRadius: radius.lg, padding: spacing[4], marginLeft: spacing[5], gap: spacing[2], borderWidth: 1, borderColor: colors.border }, recentName: { fontFamily: typography.fontFamily.ui, fontSize: typography.fontSize.base, fontWeight: typography.fontWeight.semibold, color: colors.textPrimary }, recentMeta: { fontFamily: typography.fontFamily.ui, fontSize: typography.fontSize.xs, color: colors.textSecondary }, recentBottom: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }, recentWeight: { fontFamily: typography.fontFamily.ui, fontSize: typography.fontSize.lg, fontWeight: typography.fontWeight.bold, color: colors.textPrimary }, inventoryRow: { flexDirection: 'row', alignItems: 'center', gap: spacing[3], backgroundColor: colors.bgSurface, borderRadius: radius.lg, padding: spacing[4], borderWidth: 1, borderColor: colors.border }, inventoryInfo: { flex: 1, gap: 2 }, inventoryName: { fontFamily: typography.fontFamily.ui, fontSize: typography.fontSize.base, fontWeight: typography.fontWeight.semibold, color: colors.textPrimary }, inventoryMeta: { fontFamily: typography.fontFamily.ui, fontSize: typography.fontSize.xs, color: colors.textSecondary }, inventoryRight: { alignItems: 'flex-end', gap: spacing[1] }, inventoryWeight: { fontFamily: typography.fontFamily.ui, fontSize: typography.fontSize.base, fontWeight: typography.fontWeight.bold, color: colors.textPrimary }, pesarBtn: { flexDirection: 'row', alignItems: 'center', gap: 4 }, pesarText: { fontFamily: typography.fontFamily.ui, fontSize: typography.fontSize.xs, color: colors.accent }, materialRow: { flexDirection: 'row', alignItems: 'center', gap: spacing[3], paddingVertical: spacing[3], borderBottomWidth: 1, borderBottomColor: colors.border }, materialDot: { width: 8, height: 8, borderRadius: radius.full }, materialName: { flex: 1, fontFamily: typography.fontFamily.ui, fontSize: typography.fontSize.base, color: colors.textPrimary }, materialCount: { fontFamily: typography.fontFamily.ui, fontSize: typography.fontSize.sm, color: colors.textSecondary }, materialWeight: { fontFamily: typography.fontFamily.ui, fontSize: typography.fontSize.base, fontWeight: typography.fontWeight.bold, color: colors.textPrimary, minWidth: 60, textAlign: 'right' }, lowStockCard: { flexDirection: 'row', alignItems: 'center', gap: spacing[3], backgroundColor: colors.bgSurface, borderRadius: radius.lg, padding: spacing[4], borderWidth: 1, borderColor: colors.border, overflow: 'hidden' }, lowStockAccent: { position: 'absolute', left: 0, top: 0, bottom: 0, width: 3 }, lowStockInfo: { flex: 1, gap: 2 }, lowStockName: { fontFamily: typography.fontFamily.ui, fontSize: typography.fontSize.base, fontWeight: typography.fontWeight.semibold, color: colors.textPrimary }, lowStockMeta: { fontFamily: typography.fontFamily.ui, fontSize: typography.fontSize.xs, color: colors.textSecondary }, lowStockRight: { alignItems: 'flex-end', gap: spacing[1] }, lowStockWeight: { fontFamily: typography.fontFamily.ui, fontSize: typography.fontSize.base, fontWeight: typography.fontWeight.bold }, empty: { flex: 1, alignItems: 'center', justifyContent: 'center', gap: spacing[3], marginTop: spacing[16] }, emptyTitle: { fontFamily: typography.fontFamily.ui, fontSize: typography.fontSize.lg, fontWeight: typography.fontWeight.semibold, color: colors.textPrimary }, emptyText: { fontFamily: typography.fontFamily.ui, fontSize: typography.fontSize.sm, color: colors.textSecondary, textAlign: 'center' }, });