erp-transportistas-v2/mobile/src/screens/ChecklistScreen.tsx
Adrian Flores Cortes 6ed7f9e2ec [BACKUP] Pre-restructure workspace backup 2026-01-29
- Updated docs and inventory files
- Added new architecture docs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 17:35:54 -06:00

284 lines
6.9 KiB
TypeScript

/**
* Checklist Screen
* ERP Transportistas
* Sprint S8 - TASK-007
*
* Pre-trip checklist screen.
*/
import React, { useEffect, useState } from 'react';
import {
View,
Text,
StyleSheet,
ScrollView,
TouchableOpacity,
ActivityIndicator,
Alert,
} from 'react-native';
import { useNavigation, useRoute } from '@react-navigation/native';
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
import type { RouteProp } from '@react-navigation/native';
import { Ionicons } from '@expo/vector-icons';
import { useViajeStore } from '../store';
import type { RootStackParamList, ChecklistItem } from '../types';
type NavigationProp = NativeStackNavigationProp<RootStackParamList>;
type ChecklistRouteProp = RouteProp<RootStackParamList, 'Checklist'>;
export function ChecklistScreen(): JSX.Element {
const navigation = useNavigation<NavigationProp>();
const route = useRoute<ChecklistRouteProp>();
const {
checklist,
cargarChecklist,
completarItemChecklist,
guardarChecklist,
isLoading,
} = useViajeStore();
const [isSaving, setIsSaving] = useState(false);
useEffect(() => {
cargarChecklist(route.params.viajeId);
}, [route.params.viajeId]);
const toggleItem = (itemId: string, currentState: boolean): void => {
completarItemChecklist(itemId, !currentState);
};
const allRequiredCompleted = (): boolean => {
return checklist
.filter((item) => item.requerido)
.every((item) => item.completado);
};
const handleSave = async (): Promise<void> => {
if (!allRequiredCompleted()) {
Alert.alert(
'Items Pendientes',
'Debes completar todos los items requeridos antes de continuar'
);
return;
}
setIsSaving(true);
try {
await guardarChecklist();
Alert.alert('Éxito', 'Checklist guardado correctamente', [
{ text: 'OK', onPress: () => navigation.goBack() },
]);
} catch (error) {
Alert.alert('Error', 'No se pudo guardar el checklist');
} finally {
setIsSaving(false);
}
};
const renderItem = (item: ChecklistItem): JSX.Element => (
<TouchableOpacity
key={item.id}
style={[styles.itemContainer, item.completado && styles.itemCompleted]}
onPress={() => toggleItem(item.id, item.completado)}
>
<View style={styles.itemCheckbox}>
{item.completado ? (
<Ionicons name="checkbox" size={28} color="#22c55e" />
) : (
<Ionicons name="square-outline" size={28} color="#94a3b8" />
)}
</View>
<View style={styles.itemContent}>
<Text style={[styles.itemDescription, item.completado && styles.itemTextCompleted]}>
{item.descripcion}
</Text>
{item.requerido && (
<Text style={styles.itemRequired}>Requerido</Text>
)}
</View>
{item.foto && (
<Ionicons name="camera" size={20} color="#3b82f6" />
)}
</TouchableOpacity>
);
if (isLoading) {
return (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color="#2563eb" />
<Text style={styles.loadingText}>Cargando checklist...</Text>
</View>
);
}
const completedCount = checklist.filter((item) => item.completado).length;
const totalCount = checklist.length;
const progress = totalCount > 0 ? (completedCount / totalCount) * 100 : 0;
return (
<View style={styles.container}>
{/* Progress Header */}
<View style={styles.header}>
<Text style={styles.headerTitle}>Checklist Pre-viaje</Text>
<Text style={styles.headerProgress}>
{completedCount} de {totalCount} completados
</Text>
<View style={styles.progressBar}>
<View style={[styles.progressFill, { width: `${progress}%` }]} />
</View>
</View>
{/* Checklist Items */}
<ScrollView style={styles.content}>
{checklist.length === 0 ? (
<View style={styles.emptyContainer}>
<Ionicons name="clipboard-outline" size={48} color="#94a3b8" />
<Text style={styles.emptyText}>No hay items en el checklist</Text>
</View>
) : (
checklist.map(renderItem)
)}
</ScrollView>
{/* Save Button */}
<View style={styles.footer}>
<TouchableOpacity
style={[
styles.saveButton,
(!allRequiredCompleted() || isSaving) && styles.saveButtonDisabled,
]}
onPress={handleSave}
disabled={!allRequiredCompleted() || isSaving}
>
{isSaving ? (
<ActivityIndicator color="#ffffff" />
) : (
<>
<Ionicons name="checkmark-circle" size={24} color="#ffffff" />
<Text style={styles.saveButtonText}>Guardar y Continuar</Text>
</>
)}
</TouchableOpacity>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f1f5f9',
},
loadingContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
loadingText: {
marginTop: 12,
fontSize: 16,
color: '#64748b',
},
header: {
backgroundColor: '#ffffff',
padding: 16,
borderBottomWidth: 1,
borderBottomColor: '#e2e8f0',
},
headerTitle: {
fontSize: 20,
fontWeight: 'bold',
color: '#1e293b',
},
headerProgress: {
fontSize: 14,
color: '#64748b',
marginTop: 4,
},
progressBar: {
height: 6,
backgroundColor: '#e2e8f0',
borderRadius: 3,
marginTop: 12,
overflow: 'hidden',
},
progressFill: {
height: '100%',
backgroundColor: '#22c55e',
borderRadius: 3,
},
content: {
flex: 1,
padding: 16,
},
emptyContainer: {
alignItems: 'center',
paddingVertical: 48,
},
emptyText: {
fontSize: 16,
color: '#64748b',
marginTop: 12,
},
itemContainer: {
backgroundColor: '#ffffff',
borderRadius: 12,
padding: 16,
marginBottom: 8,
flexDirection: 'row',
alignItems: 'center',
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.05,
shadowRadius: 2,
elevation: 1,
},
itemCompleted: {
backgroundColor: '#f0fdf4',
borderWidth: 1,
borderColor: '#bbf7d0',
},
itemCheckbox: {
marginRight: 12,
},
itemContent: {
flex: 1,
},
itemDescription: {
fontSize: 16,
color: '#1e293b',
},
itemTextCompleted: {
color: '#16a34a',
},
itemRequired: {
fontSize: 12,
color: '#f59e0b',
marginTop: 4,
},
footer: {
padding: 16,
backgroundColor: '#ffffff',
borderTopWidth: 1,
borderTopColor: '#e2e8f0',
},
saveButton: {
backgroundColor: '#22c55e',
borderRadius: 12,
padding: 16,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
},
saveButtonDisabled: {
backgroundColor: '#86efac',
},
saveButtonText: {
color: '#ffffff',
fontSize: 16,
fontWeight: '600',
marginLeft: 8,
},
});