feat(android): app de campo móvil+tablet (UI, mapa, fotos, auto-sync, reconciliación)
Fase 0: config Android (package, permisos, orientación), eas.json (APK), deps nativas (react-native-maps, image-picker, location), app.config.js para la key de Google Maps por secreto EAS. Fase 1: capa responsive (useLayout) + componente MasterDetail (dos paneles en tablet, navegación en móvil). Fase 2: pantallas funcionales — detalle de proyecto con secciones Fases/Features/ Incidencias, edición de progreso/estado, formulario de inspección dinámico desde plantilla, incidencias maestro-detalle (checklist + comentarios), alta de incidencia; gating por permisos Spatie. Fase 3: fotos (cámara/galería) → cola de media, con miniaturas pendientes/sync. Fase 4: mapa de features (Google Maps) con geometría GeoJSON y selección. Fase 5: auto-sync (foreground/reconexión/intervalo, con candado) + reconciliación de creaciones offline (id temporal negativo → server_id, remapeo de FKs hijas). Fase 6: revisión de conflictos/errores del outbox, indicadores y APK preview. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
4e9c7d059f
commit
9bcc51e3b2
@@ -1,19 +1,22 @@
|
||||
import React from 'react';
|
||||
import { ActivityIndicator, StyleSheet, Text, View } from 'react-native';
|
||||
import { ActivityIndicator, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||
import { OutboxCounts } from '../db/outbox';
|
||||
import { useIsOnline } from '../net/connectivity';
|
||||
|
||||
export function SyncStatusBar({
|
||||
counts,
|
||||
syncing,
|
||||
onPress,
|
||||
}: {
|
||||
counts: OutboxCounts;
|
||||
syncing: boolean;
|
||||
onPress?: () => void;
|
||||
}) {
|
||||
const online = useIsOnline();
|
||||
const pendientes = counts.pending + counts.mediaPending;
|
||||
const hasProblems = counts.conflict > 0 || counts.error > 0;
|
||||
|
||||
return (
|
||||
const content = (
|
||||
<View style={[styles.bar, { backgroundColor: online ? '#1f6f43' : '#8a6d00' }]}>
|
||||
{syncing && <ActivityIndicator color="#fff" size="small" />}
|
||||
<Text style={styles.text}>
|
||||
@@ -22,8 +25,18 @@ export function SyncStatusBar({
|
||||
{counts.conflict > 0 ? ` · ${counts.conflict} conflicto(s)` : ''}
|
||||
{counts.error > 0 ? ` · ${counts.error} error(es)` : ''}
|
||||
</Text>
|
||||
{hasProblems && onPress ? <Text style={styles.chevron}>revisar ›</Text> : null}
|
||||
</View>
|
||||
);
|
||||
|
||||
if (onPress) {
|
||||
return (
|
||||
<TouchableOpacity activeOpacity={0.8} onPress={onPress}>
|
||||
{content}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
@@ -34,5 +47,6 @@ const styles = StyleSheet.create({
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 8,
|
||||
},
|
||||
text: { color: '#fff', fontSize: 13, fontWeight: '600' },
|
||||
text: { color: '#fff', fontSize: 13, fontWeight: '600', flex: 1 },
|
||||
chevron: { color: '#fff', fontSize: 12, fontWeight: '700', textDecorationLine: 'underline' },
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user