feat: scaffold offline-first mobile app (RN+Expo, expo-sqlite)
Capa API tipada de los 8 endpoints, BD local espejo del bundle + outbox (operaciones y media) + cursor de sync, motor runSync (PUSH /sync -> PUSH /media -> PULL bundle?since) con idempotencia por uuid y last-write-wins, mutaciones de alto nivel (write local + encolar), sesion con token en SecureStore, conectividad NetInfo y UI minima (Login -> Proyectos -> Detalle). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
3f454b59a5
commit
4e9c7d059f
@@ -0,0 +1,38 @@
|
||||
import React from 'react';
|
||||
import { ActivityIndicator, StyleSheet, Text, View } from 'react-native';
|
||||
import { OutboxCounts } from '../db/outbox';
|
||||
import { useIsOnline } from '../net/connectivity';
|
||||
|
||||
export function SyncStatusBar({
|
||||
counts,
|
||||
syncing,
|
||||
}: {
|
||||
counts: OutboxCounts;
|
||||
syncing: boolean;
|
||||
}) {
|
||||
const online = useIsOnline();
|
||||
const pendientes = counts.pending + counts.mediaPending;
|
||||
|
||||
return (
|
||||
<View style={[styles.bar, { backgroundColor: online ? '#1f6f43' : '#8a6d00' }]}>
|
||||
{syncing && <ActivityIndicator color="#fff" size="small" />}
|
||||
<Text style={styles.text}>
|
||||
{online ? 'En línea' : 'Sin conexión'}
|
||||
{pendientes > 0 ? ` · ${pendientes} en cola` : ' · al día'}
|
||||
{counts.conflict > 0 ? ` · ${counts.conflict} conflicto(s)` : ''}
|
||||
{counts.error > 0 ? ` · ${counts.error} error(es)` : ''}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
bar: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 8,
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 8,
|
||||
},
|
||||
text: { color: '#fff', fontSize: 13, fontWeight: '600' },
|
||||
});
|
||||
Reference in New Issue
Block a user