# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. > **Cursor users:** detailed, agent-ready versions of these guidelines live in [`.cursor/rules/`](.cursor/rules/). Each rule file is scoped and auto-applied by Cursor AI during code generation and review. ## Project Overview Humand is a React Native (0.83 + Expo 55) mobile application for workforce management. It uses TypeScript, Redux Toolkit + Saga for cross-module state, React Query v5 for server state, Zustand for lightweight local state, and Zod + react-hook-form for forms. ## Common Commands ### Development - `yarn start` - Start Metro bundler with dev server (supports HMR) - `yarn android:dev` - Run Android dev build - `yarn android:stg` - Run Android staging build - `yarn android:prod` - Run Android production build - `yarn ios:dev` - Run iOS dev build - `yarn ios:stg` - Run iOS staging build - `yarn ios:prod` - Run iOS production build ### Build - `yarn build:android.dev` - Build Android dev APK - `yarn build:android.stg` - Build Android staging APK - `yarn build:android.prod` - Build Android production APK ### Testing & Quality - `yarn test` - Run Jest tests - `yarn test:coverage` - Run tests with coverage - `yarn test:ci` - Run tests in CI mode - `yarn lint` - Run ESLint - `yarn lint-fix` - Auto-fix ESLint issues - `yarn check-types` - Run TypeScript type checking ### E2E Testing (Maestro) - `yarn e2e:android:dev` - Run E2E tests on Android dev - `yarn e2e:ios:dev` - Run E2E tests on iOS dev - `yarn e2e:android:stg` - Run E2E tests on Android staging - `yarn e2e:ios:stg` - Run E2E tests on iOS staging - `yarn e2e:parallel` - Run E2E tests in parallel ### Cleanup - `yarn full-clean` - Deep clean: removes ios/build, ios/Pods, node_modules, reinstalls dependencies ## Architecture ### Directory Structure ``` app/ ├── assets/ - Static assets (fonts, images, audio) ├── config/ - Configuration (API instances, tokens, i18n, Sentry, etc.) ├── db/ - Local database (op-sqlite) ├── interfaces/ - Shared TypeScript interfaces ├── modules/ - Feature modules (see below) ├── navigation/ - Navigation configuration and Screens enum ├── redux/ - Redux store, slices, sagas (cross-module state) ├── services/ - Shared API services ├── shared/ - Shared code (components, hooks, utils, theme, schemas) └── stores/ - Zustand stores (lightweight local state) ``` ### Feature Module Structure Each module under `app/modules//` follows this pattern: ``` app/modules// ├── screens/ - Screen components (orchestrators only) ├── components/ - Module-specific UI components ├── hooks/ - Data-fetching hooks (wrapping React Query/Redux) ├── navigation/ - Navigator definition + screen param types ├── redux/ - Redux slices + sagas (optional, for module state) ├── store/ - Zustand stores (optional, for local state) ├── interfaces.ts - All types for this module └── services.ts - API calls (no state logic) ``` **Key principles:** - Screens are orchestrators: mount data hooks, pass data to components - Components should never call services directly - always go through hooks - Non-trivial types go in `interfaces.ts`, not inline ### Path Aliases Use these aliases for all imports (sorted: external → internal → relative): | Alias | Target | | ------------- | -------------------------------------- | | `@shared` | shared components, hooks, utils, theme | | `@modules` | feature modules | | `@components` | shared components (shorthand) | | `@hooks` | shared hooks (shorthand) | | `@redux` | Redux store, slices, sagas | | `@navigation` | navigation config + Screens enum | | `@config` | API instances, tokens, i18n setup | | `@interfaces` | shared TypeScript interfaces | | `@services` | shared API services | | `@stores` | Zustand stores | | `@assets/*` | app/assets/\* | ## State Management Decision Guide Choose the right tool based on data nature: | Data type | Tool | | ------------------------------------------------------------ | ------------------------- | | Server state (API data, lists, pagination) | React Query | | Persisted cross-module state (auth, user, instance) | Redux Toolkit + Saga | | Lightweight local/UI state (theme, feature flags, ephemeral) | Zustand | | Transient component state | `useState` / `useReducer` | ### React Query (v5) Use the project's custom wrappers from `@shared/hooks/queries-v5` **only in modules that have been migrated**. In modules not yet migrated, continue using React Query v3 (`react-query`). In migrated modules, use the wrappers from `@shared/hooks/queries-v5`, not TanStack directly: - `useQuery` - Standard data fetching with optional persistence - `useInfiniteQuery` - Offset-based pagination - `useCursorInfinityQuery` - Cursor-based pagination - `useTimestampInfinityQuery` - Timestamp-based pagination **Important:** Never store React Query data in Redux - React Query IS the cache. ```ts import {useQuery} from '@shared/hooks/queries-v5'; const {data, isLoading} = useQuery( ['learning', 'courses'], () => getCourses(), {onError: onFetchError}, ); ``` ### Redux Toolkit + Saga - Use `createSlice` with `PayloadAction` - Mutate state directly (Immer handles immutability) - Always add `extraReducers` case for `GlobalActionTypes.RESET` - Async side effects go in sagas using `call`, `put`, `takeLatest` / `takeEvery` - Export root saga from module's `redux/` and register in root saga ### Zustand - Use for non-persisted, local feature state - Store definition in `store/index.ts` (module) or `app/stores/` (global) - Use `zustand/persist` with `EncryptedStorage` only when persistence is required ## Styling & Theme - Use `useTheme` from `@shared/theme`; destructure what you need: ```ts const {theme, spacing, iconSizes, borderRadius} = useTheme(); ``` - Never hardcode pixel values - use constants: - `SPACING.x2` instead of `16` - `BORDER_RADIUS.m` instead of `8` - `ICON_SIZES.x6` instead of `24` - Access colors via theme paths: `theme.background.layout.default`, `theme.text.primary` - Static styles: `StyleSheet.create()` in co-located `styles.ts` - Dynamic (theme-dependent) styles: style arrays at render time: ```tsx style={[styles.container, {backgroundColor: theme.background.layout.default}]} ``` ## Forms All forms use `react-hook-form` with `zod` validation via `zodResolver`. ```ts import {z} from 'zod'; import {useMemo} from 'react'; import {useTranslation} from 'react-i18next'; import {requiredString, getMaxErrorText} from '@shared/schemas'; const schema = useMemo( () => z.object({ title: requiredString().max(100, getMaxErrorText(100)), description: z.string().optional(), }), [t], ); const methods = useForm({ mode: 'onBlur', // Always use onBlur as default resolver: zodResolver(schema), defaultValues: {title: '', description: ''}, }); ``` **Rules:** - Define Zod schema inside `useMemo` when it depends on runtime values - Use shared helpers from `@shared/schemas` for common validations - Read errors from `formState.errors` - never manage in local state - Extract form logic into dedicated hook: `useMyFeatureForm` in module's `hooks/` ## Navigation - Use the `Screens` enum from `@navigation` - never raw strings: ```ts navigation.navigate(Screens.COURSE, {courseId: id}); // correct navigation.navigate('Course', {courseId: id}); // incorrect ``` - Type navigation hooks explicitly: ```ts const navigation = useNavigation>(); const route = useRoute>(); ``` - Screen param types defined in module's `navigation/` and merged into `RootStackParamList` ## E2E Testing (Maestro) **Quick Rules:** - Always start with `runFlow: ../../shared/launch-app.yml` - Use shared flows from `e2e/shared/` when available - Choose selectors in order: visible text → translation values → accessibilityLabel → testID (last resort) - Never assert exact timestamps - use regex - Prefer `extendedWaitUntil` over fixed sleeps - New CI flows must be added to `e2e/config.yml` **Run with env:** ```bash yarn e2e:run e2e/.env.dev android --config e2e/config.yml ``` ## Component Structure Every component folder follows: ``` ComponentName/ index.tsx ← Props interface + functional component styles.ts ← StyleSheet.create() using theme constants interfaces.ts ← (only when prop/data types are non-trivial) components/ ← sub-components (if needed) ``` - Props interface named `Props` (no component name prefix) - Prefer named exports over default exports - Barrel-export from `index.ts` in shared folders ## Naming Patterns - Event handlers: `onPress`, `onSubmit`, `onClose` - Callback props: `onSuccess`, `onError` (not `handle`) - Booleans: `isLoading`, `hasError`, `canSubmit`, `isVisible` - Hooks: `use` prefix (e.g., `useAuth`, `useForm`) ## Internationalization - All user-visible strings must use `react-i18next`: ```ts const {t} = useTranslation(); {t('module.key')}; ``` - Never hardcode UI strings in JSX or logic ## React Native Patterns - Use `FlatList` over `ScrollView + map` for any list beyond a few items - Use `useSafeAreaInsets()` for bottom/top padding - Bottom sheets: use `@gorhom/bottom-sheet` (never custom implementations) - Animations: use `react-native-reanimated` (avoid core `Animated`) - Gestures: use `react-native-gesture-handler` - Keyboard: use hooks from `@shared/hooks` (e.g., `useKeyboardHeight`)