FieldOps Manager is an offline-first mobile application for field collection management. It was built with Expo, TypeScript and a feature-based architecture to demonstrate local persistence, synchronization queues, typed forms and scalable mobile state management.
The app focuses on a common field operations scenario: drivers or operators need to view assigned collections, complete them in unstable network conditions and let the app synchronize changes later when connectivity is restored.
- Offline-first data flow using
expo-sqlite. - Hybrid fetching with TanStack Query v5: remote-first when online, SQLite fallback when offline.
- Persistent synchronization queue for offline updates.
- Feature-based architecture under
src/features/coletas. - File-based navigation with Expo Router.
- UI state isolated with Zustand.
- Form validation with React Hook Form and Zod.
- NativeWind styling for a corporate dashboard experience.
- Strict TypeScript configuration.
| Layer | Technology |
|---|---|
| Framework | Expo SDK 55 |
| Language | TypeScript strict mode |
| Navigation | Expo Router |
| Server/cache state | TanStack Query v5 |
| UI/session state | Zustand |
| Local database | expo-sqlite |
| Styling | NativeWind |
| Forms | React Hook Form |
| Validation | Zod |
| Native runtime | Expo Dev Client |
src
|-- app
| |-- _layout.tsx
| |-- index.tsx
| `-- coletas
| `-- [id].tsx
|-- features
| `-- coletas
| |-- components
| | |-- ColetaCard.tsx
| | |-- ColetaListSkeleton.tsx
| | |-- FinalizacaoColetaForm.tsx
| | `-- StatusBadge.tsx
| |-- hooks
| | |-- useColetas.ts
| | `-- useSyncColetas.ts
| |-- services
| | |-- coletasApi.ts
| | |-- coletasLocalService.ts
| | `-- database.ts
| |-- store
| | `-- coletasStore.ts
| `-- types
| `-- coleta.ts
`-- shared
|-- lib
| `-- queryClient.ts
`-- providers
`-- AppProviders.tsx
The project keeps business logic inside feature modules. Shared code is limited to app-level providers and infrastructure that can be reused by other features.
useColetasreads the current network state.- When online, it fetches from the mock API and upserts the response into SQLite.
- When offline, it reads exclusively from SQLite.
- Collection finalization is saved locally first.
- If the device is offline or the remote mutation fails, the payload is stored in
sync_queue. useSyncColetasflushes pending queue items when connectivity returns.
The SQLite schema is created in src/features/coletas/services/database.ts.
Tables:
coletas: local collection records and sync metadata.sync_queue: pending offline mutations waiting to be sent.
The local service in coletasLocalService.ts owns direct SQL operations. Hooks depend on this service instead of talking to SQLite directly, keeping UI and persistence concerns separated.
- Dashboard list with metrics, status filters, search, pull-to-refresh and synchronization state.
- Collection detail screen with a finalization form.
- Skeleton loading state for perceived performance.
- Offline queue indicator with manual sync action.
Install dependencies:
npm installRun TypeScript validation:
npm run typecheckStart the Expo server:
npm run startRun the Android development build:
npm run androidRun the web target:
npm run webThis project targets Expo SDK 55. During the SDK 55 transition, the Play Store version of Expo Go may still be tied to the previous SDK channel. For the most reliable local workflow, use Expo Dev Client:
npm run android
npm run startThen open the app through the generated development client instead of the Play Store Expo Go app.
| Command | Description |
|---|---|
npm run start |
Starts the Expo development server. |
npm run android |
Builds/runs the Android development client. |
npm run ios |
Builds/runs the iOS development client. |
npm run web |
Starts the web target. |
npm run typecheck |
Runs tsc --noEmit. |
- API calls are mocked in
coletasApi.tsto keep the project self-contained. - Query invalidation is centralized through
coletasQueryKeys. - The finalization form uses Zod coercion to convert text input into numeric payloads.
- The app uses conservative render settings on
FlatListfor mobile performance. - SQL writes are grouped with exclusive transactions where consistency matters.
- Add authentication and role-based route protection.
- Persist TanStack Query metadata for richer cold-start behavior.
- Add conflict resolution using server revision numbers.
- Add integration tests for the sync queue.
- Replace the mock API with a real backend adapter.