A modular React Native application for tracking sports performances, built with Expo and TypeScript.
This project demonstrates a reusable, configuration-driven module for recording and managing sports performances. The module can be easily integrated into other React Native applications.
- ✅ Performance Tracking: Record sports performances with title, date, and optional metrics (distance, duration, heart rate, notes)
- ✅ Configuration-Driven: UI and form fields controlled by validated configuration
- ✅ Type-Safe: Full TypeScript with Zod schema validation
- ✅ Modular Architecture: Separated module that can be reused in other apps
- ✅ Settings UI: Dedicated configuration screen for customizing fields and display
- ✅ Animated: Smooth transitions using React Native Reanimated
- ✅ Internationalized: Support for multiple languages (EN, CS)
- ✅ Tested: Unit tests for core logic with 70%+ coverage goal
Before you begin, ensure you have the following installed:
- Node.js: v18 or later
- npm: v9 or later (or yarn)
- Expo CLI: Install globally with
npm install -g expo-cli - iOS Simulator (Mac only) or Android Emulator or Expo Go app on your device
git clone <repository-url>
cd etnetera-performance-appnpm installThis will install all required dependencies including:
- React Native and Expo packages
- Zod for validation
- React Hook Form for form management
- i18next for internationalization
- Testing libraries (Jest, React Native Testing Library)
npm startThis will start the Expo development server and display a QR code in your terminal.
npm run iosThis will open the app in the iOS Simulator.
npm run androidThis will open the app in the Android Emulator.
npm run webThis will open the app in your default web browser.
- Install the Expo Go app from the App Store (iOS) or Play Store (Android)
- Scan the QR code displayed in your terminal
- The app will load on your device
etnetera-performance-app/
├── app/ # Expo Router app directory
│ ├── index.tsx # Main screen - Performances list with FAB
│ ├── performance/
│ │ └── [id].tsx # Add/edit form modal
│ ├── settings.tsx # Settings modal
│ └── _layout.tsx # Root layout with providers and modal routes
├── modules/ # Reusable modules
│ └── performance-tracker/ # Performance tracking module
│ ├── components/ # UI components
│ ├── context/ # React Context providers
│ ├── hooks/ # Custom hooks
│ ├── schemas/ # Zod validation schemas
│ ├── types/ # TypeScript type definitions
│ ├── utils/ # Utility functions
│ ├── locales/ # i18n translation files
│ ├── __tests__/ # Unit tests
│ └── index.ts # Public API exports
├── docs/ # Documentation
│ ├── app_spec.pdf # Original specification
│ ├── PRD.md # Product Requirements Document
│ └── ARCHITECTURE.md # Architecture documentation
├── CLAUDE.md # Claude Code guidance
├── package.json
├── tsconfig.json
└── jest.config.js
npm start- Start the Expo development servernpm run ios- Run on iOS Simulatornpm run android- Run on Android Emulatornpm run web- Run in web browser
npm run lint- Run ESLint to check code qualitynpm test- Run unit testsnpm run test:watch- Run tests in watch modenpm run test:coverage- Run tests with coverage report
npm run reset-project- Reset the project to a clean state
- Launch the app
- The main screen shows all recorded performances
- Tap any performance to edit it
- Tap the floating + button at the bottom center of the screen
- A modal will open with the form
- Fill in the required fields (Title, Date)
- Fill in optional fields if enabled (Distance, Duration, Heart Rate, Notes)
- Tap Save to create the performance
- Tap an existing performance from the list
- The edit modal will open
- Update any fields
- Tap Save to update
- Tap the gear icon in the top right corner
- Settings modal will open
- For each field (Distance, Duration, Heart Rate, Notes), you have two settings:
- Enable in Form - Allow entering/editing this field in add/edit form
- Show in List - Display this field in the performance list
- Important: "Show in List" requires "Enable in Form" to be enabled
- If you disable "Enable in Form", "Show in List" is automatically disabled
- You can enable a field in the form without showing it in the list
- Select sort order and direction in "Sort Order" section
- Changes take effect immediately
- Tap the X or swipe down to close settings
- Tap the gear icon to open Settings
- Scroll to the "Language" section
- Select your preferred language:
- English
- Čeština (Czech)
- The app language will change immediately
The performance tracker is designed as a reusable module. To integrate it into another app:
Copy the entire modules/performance-tracker directory to your project.
Ensure your project has these dependencies:
{
"dependencies": {
"zod": "^3.x",
"react-hook-form": "^7.x",
"@hookform/resolvers": "^3.x",
"react-native-reanimated": "~4.x",
"i18next": "^25.x",
"react-i18next": "^16.x"
}
}import {
ConfigurationProvider,
PerformanceProvider,
} from './modules/performance-tracker';
function App() {
return (
<ConfigurationProvider>
<PerformanceProvider>
{/* Your app content */}
</PerformanceProvider>
</ConfigurationProvider>
);
}import { PerformanceList, PerformanceForm } from './modules/performance-tracker';
// In your screen
<PerformanceList onItemPress={(id) => navigateToEdit(id)} />import { ConfigurationProvider, defaultConfiguration } from './modules/performance-tracker';
const customConfig = {
...defaultConfiguration,
fields: {
distance: { enabledInForm: true, showInList: true },
duration: { enabledInForm: false, showInList: false },
heartRate: { enabledInForm: true, showInList: false },
notes: { enabledInForm: true, showInList: false },
},
};
<ConfigurationProvider initialConfig={customConfig}>
{/* ... */}
</ConfigurationProvider># Run all tests
npm test
# Run tests in watch mode
npm run test:watch
# Run tests with coverage
npm run test:coverageThe project includes unit tests for:
- Zod schema validation
- Configuration validation
- Sorting utilities
- Formatting utilities
Coverage Target: 70%+ for core logic (schemas, utils)
No environment variables are required for this demo app.
The project uses TypeScript in strict mode. Configuration can be found in tsconfig.json.
Linting is configured via Expo's default ESLint config. Run npm run lint to check code quality.
- Framework: React Native with Expo SDK ~54
- Language: TypeScript (strict mode)
- Routing: Expo Router (file-based)
- Form Management: React Hook Form + Zod
- Validation: Zod
- Animation: React Native Reanimated ~4.1
- Internationalization: i18next + react-i18next
- Testing: Jest + React Native Testing Library
- State Management: React Context API
For detailed architecture documentation, see ARCHITECTURE.md.
Key architectural decisions:
- Modular Design: Complete separation between reusable module and demo app
- Configuration-Driven: All behavior controlled by validated schemas
- Type-Safe: Full TypeScript with Zod runtime validation
- Context-Based State: Simple, dependency-free state management
The app supports multiple languages:
- English (en)
- Czech (cs)
Language is auto-detected from device settings. Translation files are located in modules/performance-tracker/locales/.
To add a new language:
- Create a new JSON file (e.g.,
de.json) - Add translations following the structure of
en.json - Import in
locales/i18n.ts
- In-Memory Storage: Data is lost when the app restarts. No persistence implemented.
- No Delete Function: Cannot delete performances from the UI (Context method exists).
- Date Picker: Date input is not interactive (would require platform-specific implementation).
- Configuration Restart: Some configuration changes may require app restart.
- Add AsyncStorage for data persistence
- Implement delete functionality with swipe gestures
- Add cross-platform date picker
- Real-time configuration updates
- Search and filter functionality
- Performance analytics and charts
- Export to CSV/PDF
See ARCHITECTURE.md for detailed discussion of trade-offs and future improvements.
# Clear Metro cache
npx expo start --clear# Check for errors
npx tsc --noEmit# Clean install
rm -rf node_modules package-lock.json
npm install# Ensure Xcode is installed and simulators are available
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer- Ensure Android Studio is installed
- Create an AVD (Android Virtual Device) in AVD Manager
- Start the emulator manually from Android Studio
This project is private and intended for demonstration purposes.
For questions or issues related to this implementation, refer to:
- PRD.md - Product Requirements
- ARCHITECTURE.md - Architecture Documentation
- CLAUDE.md - Claude Code Guidance
Built as a test assignment for Etnetera Flow demonstrating:
- Modular React Native architecture
- TypeScript and type safety
- Configuration-driven development
- Clean code principles