Skip to content

Conversation

@ucswift
Copy link
Member

@ucswift ucswift commented Jan 16, 2026

…tocols fix.

Summary by CodeRabbit

  • New Features

    • Logout confirmation dialog with Cancel/Confirm
    • Clear‑all‑app‑data utility plus explicit image/file cleanup APIs
  • Improvements

    • Tap‑to‑open maps for units and contacts (Android maps URLs streamlined)
    • Unit status badges with color coding and contrast-aware text
    • Redesigned personnel details with translations and improved scrolling
    • Call close, notes, images UIs revamped for accessibility, dark mode, and keyboard handling
    • Protocol list now uses ProtocolId for stable item identity
  • Stability

    • Token refresh treats transient errors as non‑fatal to avoid unnecessary logouts
  • Tests

    • Expanded/updated tests for storage clearing, modals, navigation, APIs, and stores

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 16, 2026

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

📝 Walkthrough

Walkthrough

Adds a logout confirmation dialog that invokes a coordinated clearAllAppData cleanup; introduces unit-type statuses and status badges; migrates protocol identity to ProtocolId; switches Android maps to HTTPS directions URLs; implements clear-all-data registry and store-clear APIs; several UI/layout refactors and extensive test updates.

Changes

Cohort / File(s) Summary
Configuration & Dependencies
\jest.config.js`, `package.json``
Whitelist @dev-plugins/.* in Jest transformIgnorePatterns; bump @gluestack-ui/nativewind-utils from ~1.0.26^1.0.28.
Logout confirmation & clear-all-data
\src/app/(app)/settings.tsx`, `src/app/(app)/tests/settings.test.tsx`, `src/lib/storage/clear-all-data.ts`, `src/lib/storage/tests/clear-all-data.test.ts`, `src/translations/*``
Add logout confirmation dialog state and flow; implement clearAllAppData with register/unregister/resetAllStores/clear helpers; wire dialog to call cleanup and analytics; add translation keys and tests for confirm/cancel behavior.
Units: statuses, store & UI
\src/stores/units/store.ts`, `src/stores/units/tests/store.test.ts`, `src/app/(app)/home/units.tsx`, `src/components/units/unit-card.tsx`, `src/components/units/unit-details-sheet.tsx``
Add unitTypeStatuses state and fetchUnitStatuses(); fetch statuses in parallel with units; UnitCard API updated to accept statuses and render contrast-aware status badges; unit API/tests migrated to UnitInfoResultData/getUnitsInfos.
Protocol ID migration
\src/components/protocols/*`, `src/app/(app)/protocols.tsx`, `src/models/v4/callProtocols/callProtocolsResultData.ts`, `src/app/(app)/tests/protocols.test.tsx`, `src/stores/protocols/tests/store.test.ts``
Use ProtocolId instead of Id for list keys, onPress handlers, selection, and analytics; add ProtocolId property to model and tests.
Maps & contact/location actions
\src/lib/navigation.ts`, `src/lib/tests/navigation.test.ts`, `src/components/contacts/contact-details-sheet.tsx`, `src/components/units/unit-details-sheet.tsx`, `src/components/maps/*``
Android openMapsWithDirections now opens HTTPS Google Maps directions URLs (no canOpenURL); contact/location fields gain actionable press behavior (email/phone/maps) with platform-aware Linking and Alert error handling; minor formatting edits in pickers.
Auth token refresh handling
\src/stores/auth/store.tsx`, `src/stores/auth/tests/store-token-refresh.test.ts`**
Add isTransientRefreshError to classify transient network/status errors (429/502/503/504); transient refresh failures now log a warning and avoid forcing logout; non-transient errors still trigger logout; tests updated.
Calls: images/files and notes refactor
\src/stores/calls/detail-store.ts`, `src/components/calls/call-images-modal.tsx`, `src/components/calls/call-notes-modal.tsx`, `src/components/calls/full-screen-image-modal.tsx``, related tests
Add clearCallImages/clearCallFiles to call detail store; migrate several modals from BottomSheet to Modal/ScrollView/FlatList, add color-scheme handling and keyboard-aware scrolling; call-images modal now clears images on close; full-screen image uses expo-image with cachePolicy; tests/mocks updated widely.
Keyboard/scroll/layout & personnel details
\src/components/status/personnel-status-bottom-sheet.tsx`, `src/components/personnel/personnel-details-sheet.tsx`, `src/components/calls/dispatch-selection-modal.tsx`**
Add KeyboardAwareScrollView/ScrollView wrappers, restructure footers/controls, add translations and safe timestamp formatting in personnel details; UI/layout and accessibility tweaks.
Contact details & linking
\src/components/contacts/contact-details-sheet.tsx``
Make ContactField actionable via actionType and addressData; press opens mail/dialer/maps with platform-specific URL schemes and Alert error handling.
Tests, minor formatting, translations & small fixes
\src/app/tests/onboarding.test.tsx`, `src/components//tests/`, `src/translations/*`, `src/app/login/login-form.tsx``
Broad test updates (testIDs, mocks, expectations), translations added/expanded for settings/personnel/units/contacts, minor formatting and trivial default-prop whitespace change in LoginForm.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Settings
    participant LogoutDialog as Dialog
    participant Storage as clearAllAppData
    participant Auth as AuthStore

    User->>Settings: press "Logout"
    Settings->>Dialog: open confirmation
    Dialog->>User: user confirms (Yes)
    Dialog->>Storage: call clearAllAppData({resetStores, clearStorage, clearFilters, clearSecure?})
    Storage->>Storage: clear filters
    Storage->>Storage: reset registered stores (invoke resetFns)
    Storage->>Storage: clear MMKV storage
    alt clearSecure true
        Storage->>Storage: clear secure storage (async)
    end
    Storage-->>Dialog: resolve (or log errors)
    Dialog->>Auth: signOut()
    Auth-->>Dialog: sign out complete
    Dialog->>Settings: close dialog
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • github-actions

Poem

🐰 I nibble code with nimble paws,

A confirm dialog, cleared-out drawers;
Badges shining, maps that steer,
Protocol keys now crystal clear;
Tests hop, translations sing — hooray, the repo soars!

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The PR title references multiple fixes but is truncated and incomplete, making it unclear which specific changes are the primary focus. Complete the PR title to fully describe all main changes, e.g., 'Add logout confirmation dialog, fix close call navigation, update units state management, add contact linking, migrate protocols to use ProtocolId'
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🤖 Fix all issues with AI agents
In `@src/app/`(app)/__tests__/settings.test.tsx:
- Around line 275-288: The mocked ButtonText component in the test only accepts
{children} so it doesn't forward props (like onPress) used in production; update
the mock for ButtonText to accept (props) and spread {...props} onto the
rendered Text (preserving children) so event handlers and test queries (e.g.,
getByText) behave like the real ButtonText; ensure the mock still returns a Text
element and keep the Button mock using TouchableOpacity as-is.

In `@src/app/`(app)/settings.tsx:
- Around line 226-231: The logout confirmation uses a hardcoded loading label
'...' which bypasses i18n; update the ButtonText inside the Button that renders
isLoggingOut to call t('settings.logout_confirm_loading') instead of the
literal, add the new "logout_confirm_loading" key to all translation JSONs
(e.g., en.json, es.json, ar.json) and ensure translations are provided, keeping
existing keys like 'settings.logout_confirm_yes' and functions such as
handleLogoutConfirm and ButtonText unchanged.

In `@src/components/calls/close-call-bottom-sheet.tsx`:
- Around line 134-136: The current catch shows call_detail.close_call_error even
when closeCall succeeds but fetchCalls fails; update the flow in the function
that calls closeCall and fetchCalls so closeCall is awaited and its errors are
handled with the existing close_call_error message, then run
router.replace('/(app)/home/calls') and await fetchCalls in a separate
try/catch; if fetchCalls fails, either swallow/log the error or surface a
different message (e.g., call_detail.fetch_calls_error) but do not show
call_detail.close_call_error when closeCall succeeded—refer to the closeCall
call, the fetchCalls invocation, and the router.replace call to locate where to
split and handle errors independently.

In `@src/components/contacts/contact-details-sheet.tsx`:
- Around line 88-105: The switch(actionType) block in contact-details-sheet.tsx
has const declarations (addressParts, encodedAddress) inside the 'address' case
that leak scope; fix it by wrapping the entire 'address' case body in a block
(add { ... } after case 'address': and before the break) so addressParts and
encodedAddress are block-scoped and do not collide with other cases—preserve the
Platform-based url assignment and the existing break.
- Around line 110-116: The Alert.alert messages in ContactField's open-link
handling are hardcoded; wrap both the title and body strings in the i18n
translator (t) and use translation keys (e.g. t('contact.errorTitle') and
t('contact.openAppError', { action: actionType }) or similar) instead of raw
English; obtain t by destructuring useTranslation() in the ContactField
component (or accept it as a prop from the parent) and replace the two
Alert.alert calls and the console.warn message interpolation as needed to use
translated strings.

In `@src/components/personnel/personnel-details-sheet.tsx`:
- Around line 145-169: The timestamp rendering currently calls
parseDateISOString directly (for selectedPersonnel.StatusTimestamp and
selectedPersonnel.StaffingTimestamp) which can throw on malformed input; create
a small helper (e.g., safeFormatTimestamp) that takes a raw timestamp, attempts
to parse with parseDateISOString inside a try/catch, returns a formatted string
via formatDateForDisplay on success or null/empty string on failure, and use
that helper when rendering StatusTimestamp and StaffingTimestamp so the sheet
does not crash on bad timestamps and shows nothing or a fallback instead.
- Around line 88-179: This file hardcodes user-facing labels; import
useTranslation from react-i18next and call const { t } = useTranslation() in the
component, then wrap every visible string (e.g., "ID:", "Contact Information",
"Group", "Current Status", "Staffing", "Roles" and any other hardcoded text
shown via Text components) with t(...) so the component (and symbols like
selectedPersonnel, formatDateForDisplay, parseDateISOString, Badge labels) are
fully localizable following the same pattern used in unit-details-sheet.tsx.

In `@src/components/units/unit-card.tsx`:
- Around line 18-33: DEFAULT_UNIT_STATUSES contains hard-coded English labels;
replace each text value with a call to the translation function (e.g.,
t('units.status.available')) while keeping the color values unchanged so UI
strings are localized; update DEFAULT_UNIT_STATUSES (the constant) to reference
keys like 'units.status.<identifier>' for each status and ensure the component
uses the existing t() from the current useTranslation hook (and does not
double-translate already translated values when rendering the status in
UnitCard).
🧹 Nitpick comments (14)
src/app/login/login-form.tsx (1)

72-73: Variable shadowing: error shadows the prop.

The error parameter in the catch block shadows the error prop from the component signature. Consider renaming to err or trackingError for clarity.

Suggested fix
-    } catch (error) {
-      console.warn('Failed to track login server URL press analytics:', error);
+    } catch (err) {
+      console.warn('Failed to track login server URL press analytics:', err);
     }
src/components/calls/close-call-bottom-sheet.tsx (1)

33-33: Unused colorScheme variable.

The colorScheme is destructured from useColorScheme() but never used in the component. Either remove it or apply it for theming purposes.

🔧 Proposed fix
-  const { colorScheme } = useColorScheme();
+  useColorScheme(); // Enables dark mode class support via nativewind

Or remove the import and line entirely if not needed for nativewind's class-based dark mode.

src/components/calls/__tests__/close-call-bottom-sheet.test.tsx (1)

118-175: Dead code: handlers Record is never used.

The handlers Record is populated via useEffect but never read. The onValueChange is called directly on line 146. Consider removing the unused handler storage logic.

🔧 Proposed cleanup
 jest.mock('@/components/ui/select', () => {
-  // Store onValueChange handlers for each select by testID
-  const handlers: Record<string, (value: string) => void> = {};
-
   return {
     Select: ({ children, testID, selectedValue, onValueChange, ...props }: any) => {
       const React = require('react');
       const { View, TouchableOpacity, Text } = require('react-native');

-      // Store the handler
-      React.useEffect(() => {
-        if (testID && onValueChange) {
-          handlers[testID] = onValueChange;
-        }
-        return () => {
-          if (testID) {
-            delete handlers[testID];
-          }
-        };
-      }, [testID, onValueChange]);
-
       return (
         <View
           testID={testID}
           onValueChange={onValueChange}
           {...props}
         >
           {children}
           <TouchableOpacity onPress={() => onValueChange && onValueChange('1')}>
             <Text>Select Option</Text>
           </TouchableOpacity>
         </View>
       );
     },
src/stores/app/__tests__/core-store.test.ts (1)

91-108: Consolidate the duplicate store reset to avoid drift.

The store is reset twice in beforeEach (this block and the later reset around Line 225). Keeping two separate literals can diverge over time. Consider extracting a shared reset object and reusing it in both places.

♻️ Proposed refactor
+const coreStoreResetState = {
+	activeUnitId: null,
+	activeCallId: null,
+	activeCall: null,
+	activePriority: null,
+	config: null,
+	isLoading: false,
+	isInitialized: false,
+	isInitializing: false,
+	error: null,
+	activeStatuses: null,
+	activeStaffing: null,
+	currentStatus: null,
+	currentStatusValue: null,
+	currentStaffing: null,
+	currentStaffingValue: null,
+};
+
 describe('Core Store', () => {
 	beforeEach(() => {
 		// Clear all mocks before each test
 		jest.clearAllMocks();

 		// Reset store state between tests
-		useCoreStore.setState({
-			activeUnitId: null,
-			activeCallId: null,
-			activeCall: null,
-			activePriority: null,
-			config: null,
-			isLoading: false,
-			isInitialized: false,
-			isInitializing: false,
-			error: null,
-			activeStatuses: null,
-			activeStaffing: null,
-			currentStatus: null,
-			currentStatusValue: null,
-			currentStaffing: null,
-			currentStaffingValue: null,
-		});
+		useCoreStore.setState(coreStoreResetState);

 		// Setup default mock returns
-		// Reset store state by creating a fresh instance
-		useCoreStore.setState({
-			activeCallId: null,
-			activeCall: null,
-			activePriority: null,
-			config: null,
-			isLoading: false,
-			isInitialized: false,
-			isInitializing: false,
-			error: null,
-			activeStatuses: null,
-			activeStaffing: null,
-			currentStatus: null,
-			currentStatusValue: null,
-			currentStaffing: null,
-			currentStaffingValue: null,
-		});
+		// Reset store state by creating a fresh instance
+		useCoreStore.setState(coreStoreResetState);
src/components/protocols/protocol-card.tsx (1)

17-19: Consider using useCallback for the press handler.

The inline anonymous function () => onPress(protocol.ProtocolId) creates a new reference on each render. As per coding guidelines, wrap event handlers with useCallback to prevent unnecessary re-renders.

♻️ Proposed refactor
+import { useCallback } from 'react';
 import { Pressable } from 'react-native';

 // ... other imports

 export const ProtocolCard: React.FC<ProtocolCardProps> = ({ protocol, onPress }) => {
+  const handlePress = useCallback(() => {
+    onPress(protocol.ProtocolId);
+  }, [onPress, protocol.ProtocolId]);
+
   return (
-    <Pressable onPress={() => onPress(protocol.ProtocolId)} testID={`protocol-card-${protocol.ProtocolId}`}>
+    <Pressable onPress={handlePress} testID={`protocol-card-${protocol.ProtocolId}`}>
src/lib/navigation.ts (1)

133-138: Minor: Clarify comment about AndroidManifest queries.

The comment on line 134 is slightly misleading — HTTPS URLs don't actually require AndroidManifest <queries> declarations since they're handled by the system browser by default. The comment could simply state that HTTPS URLs are always openable on Android.

📝 Suggested comment clarification
-    // On Android, we use HTTPS URLs which don't need canOpenURL check
-    // HTTPS scheme is already declared in AndroidManifest queries
+    // On Android, HTTPS URLs are always openable (handled by browser or Maps app)
+    // so we skip the canOpenURL check which can fail for custom schemes
     if (Platform.OS === 'android') {
src/stores/units/store.ts (1)

78-86: Silent failure pattern is acceptable but consider structured logging.

The silent failure with console.warn is appropriate since statuses are an optional enhancement. However, per the coding guidelines, consider using the project's logger for consistency with other error handling patterns in the codebase.

♻️ Suggested improvement using structured logger
+import { logger } from '@/lib/logging';
+
 fetchUnitStatuses: async () => {
   try {
     const response = await getAllUnitStatuses();
     set({ unitTypeStatuses: response.Data || [] });
   } catch (error) {
     // Silently fail - statuses are optional enhancement
-    console.warn('Failed to fetch unit statuses:', error);
+    logger.warn({
+      message: 'Failed to fetch unit statuses',
+      context: { error: error instanceof Error ? error.message : String(error) },
+    });
   }
 },
src/stores/auth/__tests__/store-token-refresh.test.ts (1)

231-249: Good coverage for 503 Service Unavailable as transient error.

Testing 503 as a transient error is important for resilience. The test correctly verifies that temporary server issues don't force a logout.

Consider adding a test for other transient HTTP status codes (e.g., 502, 504) if they should also be treated as transient.

src/components/units/unit-details-sheet.tsx (2)

98-123: Well-implemented maps navigation handler with proper error handling.

The handleOpenMaps callback correctly:

  1. Validates coordinates before proceeding
  2. Wraps analytics in try/catch to prevent UI breakage
  3. Handles navigation errors gracefully with .catch()
  4. Has correct dependencies in useCallback

One minor note: the .catch() handler logs but doesn't provide user feedback if maps fails to open. Consider showing a toast/alert for better UX.

♻️ Optional: Add user feedback on navigation failure
 // Use the navigation utility to open maps
-openMapsWithDirections(latitude, longitude, selectedUnit.Name).catch((error) => {
-  console.warn('Failed to open maps:', error);
-});
+openMapsWithDirections(latitude, longitude, selectedUnit.Name).then((success) => {
+  if (!success) {
+    // Could show a toast or alert here
+    console.warn('Failed to open maps application');
+  }
+}).catch((error) => {
+  console.warn('Failed to open maps:', error);
+});

170-181: Good implementation of interactive location with accessibility considerations.

The Pressable wrapper with visual feedback (active states) and hint text provides a good user experience. The testID="location-press" enables testing.

Consider adding accessibility props for screen reader users.

♻️ Optional: Add accessibility props
-<Pressable onPress={handleOpenMaps} testID="location-press">
+<Pressable 
+  onPress={handleOpenMaps} 
+  testID="location-press"
+  accessibilityRole="button"
+  accessibilityLabel={t('units.tapToOpenMaps')}
+  accessibilityHint={`${t('units.coordinates')}: ${selectedUnit.Latitude}, ${selectedUnit.Longitude}`}
+>
src/components/status/personnel-status-bottom-sheet.tsx (1)

449-457: Redundant ternary for icon color.

The color value colorScheme === 'dark' ? '#fff' : '#fff' evaluates to '#fff' regardless of the color scheme. Simplify to just '#fff'.

✨ Suggested simplification
-                  <ArrowRight size={16} color={colorScheme === 'dark' ? '#fff' : '#fff'} />
+                  <ArrowRight size={16} color="#fff" />
src/lib/storage/clear-all-data.ts (1)

86-102: Minor: Filter clearing is redundant with full MMKV clear.

clearFilterOptions deletes specific MMKV keys, but clearMMKVStorage subsequently calls storage.clearAll(). The individual filter clears are effectively redundant when both flags are true. This isn't harmful, but if you want the granular logging for filters, consider documenting this behavior or skipping individual filter clearing when clearStorage is also true.

src/lib/storage/__tests__/clear-all-data.test.ts (1)

51-82: Missing cleanup for registered stores in registration tests.

The stores 'testStore' and 'anotherStore' are registered but not unregistered after the tests. This could affect subsequent tests since the registry is module-level state. Consider adding cleanup:

♻️ Add cleanup to registration tests
   describe('registerStoreReset', () => {
+    afterEach(() => {
+      unregisterStoreReset('testStore');
+      unregisterStoreReset('anotherStore');
+    });
+
     it('should register a store reset function', () => {
       const mockResetFn = jest.fn();
       registerStoreReset('testStore', mockResetFn);
src/app/(app)/home/units.tsx (1)

96-100: Memoize renderItem to avoid new function per render.

Inline renderItem functions can cause unnecessary re-renders in FlashList. Consider extracting it with useCallback. As per coding guidelines, avoid anonymous functions in renderItem.

♻️ Suggested refactor
-          ) : filteredUnits.length > 0 ? (
+          ) : filteredUnits.length > 0 ? (
             <FlashList
               data={filteredUnits}
               keyExtractor={(item, index) => item.UnitId || `unit-${index}`}
-              renderItem={({ item }) => <UnitCard unit={item} unitTypeStatuses={unitTypeStatuses} onPress={selectUnit} />}
+              renderItem={renderUnitItem}
               showsVerticalScrollIndicator={false}
               contentContainerStyle={{ paddingBottom: 100 }}
               refreshControl={<RefreshControl refreshing={refreshing} onRefresh={handleRefresh} />}
             />
           ) : (
+  const renderUnitItem = React.useCallback(
+    ({ item }: { item: typeof units[number] }) => (
+      <UnitCard unit={item} unitTypeStatuses={unitTypeStatuses} onPress={selectUnit} />
+    ),
+    [selectUnit, unitTypeStatuses]
+  );
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c9a147d and 087cabd.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (36)
  • jest.config.js
  • package.json
  • src/app/(app)/__tests__/protocols.test.tsx
  • src/app/(app)/__tests__/settings.test.tsx
  • src/app/(app)/home/units.tsx
  • src/app/(app)/protocols.tsx
  • src/app/(app)/settings.tsx
  • src/app/__tests__/onboarding.test.tsx
  • src/app/login/login-form.tsx
  • src/components/calls/__tests__/close-call-bottom-sheet.test.tsx
  • src/components/calls/close-call-bottom-sheet.tsx
  • src/components/calls/dispatch-selection-modal.tsx
  • src/components/contacts/contact-details-sheet.tsx
  • src/components/maps/full-screen-location-picker.tsx
  • src/components/maps/location-picker.tsx
  • src/components/messages/__tests__/message-details-sheet.test.tsx
  • src/components/personnel/personnel-details-sheet.tsx
  • src/components/protocols/protocol-card.tsx
  • src/components/protocols/protocol-details-sheet.tsx
  • src/components/status/personnel-status-bottom-sheet.tsx
  • src/components/units/unit-card.tsx
  • src/components/units/unit-details-sheet.tsx
  • src/lib/__tests__/navigation.test.ts
  • src/lib/navigation.ts
  • src/lib/storage/__tests__/clear-all-data.test.ts
  • src/lib/storage/clear-all-data.ts
  • src/models/v4/callProtocols/callProtocolsResultData.ts
  • src/stores/app/__tests__/core-store.test.ts
  • src/stores/auth/__tests__/store-token-refresh.test.ts
  • src/stores/auth/store.tsx
  • src/stores/protocols/__tests__/store.test.ts
  • src/stores/units/__tests__/store.test.ts
  • src/stores/units/store.ts
  • src/translations/ar.json
  • src/translations/en.json
  • src/translations/es.json
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx}: Write concise, type-safe TypeScript code
Use camelCase for variable and function names
Use TypeScript for all components and favor interfaces for props and state
Avoid using any; use precise types
Use React Navigation for navigation and deep linking following best practices
Handle errors gracefully and provide user feedback
Implement proper offline support (caching, queueing, retries)
Use Expo SecureStore for sensitive data storage
Use zustand for state management
Use react-hook-form for form handling
Use react-query for data fetching and caching
Use react-native-mmkv for local storage
Use axios for API requests

**/*.{ts,tsx}: Write concise, type-safe TypeScript code
Use camelCase for variable and function names
Use TypeScript for all components, favoring interfaces for props and state
Avoid using any; strive for precise types
Ensure support for dark mode and light mode
Handle errors gracefully and provide user feedback
Use react-query for data fetching
Use react-i18next for internationalization
Use react-native-mmkv for local storage
Use axios for API requests

Files:

  • src/components/maps/full-screen-location-picker.tsx
  • src/components/maps/location-picker.tsx
  • src/app/(app)/protocols.tsx
  • src/app/__tests__/onboarding.test.tsx
  • src/components/contacts/contact-details-sheet.tsx
  • src/app/login/login-form.tsx
  • src/models/v4/callProtocols/callProtocolsResultData.ts
  • src/app/(app)/home/units.tsx
  • src/components/calls/dispatch-selection-modal.tsx
  • src/components/units/unit-details-sheet.tsx
  • src/app/(app)/settings.tsx
  • src/stores/app/__tests__/core-store.test.ts
  • src/app/(app)/__tests__/protocols.test.tsx
  • src/lib/navigation.ts
  • src/lib/storage/__tests__/clear-all-data.test.ts
  • src/stores/auth/__tests__/store-token-refresh.test.ts
  • src/stores/auth/store.tsx
  • src/lib/__tests__/navigation.test.ts
  • src/stores/protocols/__tests__/store.test.ts
  • src/lib/storage/clear-all-data.ts
  • src/components/personnel/personnel-details-sheet.tsx
  • src/app/(app)/__tests__/settings.test.tsx
  • src/components/protocols/protocol-details-sheet.tsx
  • src/stores/units/store.ts
  • src/components/status/personnel-status-bottom-sheet.tsx
  • src/components/units/unit-card.tsx
  • src/components/messages/__tests__/message-details-sheet.test.tsx
  • src/components/calls/__tests__/close-call-bottom-sheet.test.tsx
  • src/components/protocols/protocol-card.tsx
  • src/components/calls/close-call-bottom-sheet.tsx
  • src/stores/units/__tests__/store.test.ts
**/*.tsx

📄 CodeRabbit inference engine (.cursorrules)

**/*.tsx: Use functional components and React hooks instead of class components
Use PascalCase for React component names
Use React.FC for defining functional components with props
Minimize useEffect/useState usage and avoid heavy computations during render
Use React.memo for components with static props to prevent unnecessary re-renders
Optimize FlatList with removeClippedSubviews, maxToRenderPerBatch, and windowSize
Provide getItemLayout to FlatList when items have consistent size
Avoid anonymous functions in renderItem or event handlers; define callbacks with useCallback or outside render
Use gluestack-ui for styling where available from components/ui; otherwise, style via StyleSheet.create or styled-components
Ensure responsive design across screen sizes and orientations
Use react-native-fast-image for image handling instead of the default Image where appropriate
Wrap all user-facing text in t() from react-i18next for translations
Support dark mode and light mode in UI components
Use @rnmapbox/maps for maps or navigation features
Use lucide-react-native for icons directly; do not use the gluestack-ui icon component
Use conditional rendering with the ternary operator (?:) instead of &&

**/*.tsx: Use functional components and hooks over class components
Ensure components are modular, reusable, and maintainable
Ensure all components are mobile-friendly, responsive, and support both iOS and Android
Use PascalCase for component names
Utilize React.FC for defining functional components with props
Minimize useEffect, useState, and heavy computations inside render
Use React.memo for components with static props to prevent unnecessary re-renders
Optimize FlatList with removeClippedSubviews, maxToRenderPerBatch, and windowSize
Use getItemLayout for FlatList when items have consistent size
Avoid anonymous functions in renderItem or event handlers to prevent re-renders
Ensure responsive design for different screen sizes and orientations
Optimize image handling using rea...

Files:

  • src/components/maps/full-screen-location-picker.tsx
  • src/components/maps/location-picker.tsx
  • src/app/(app)/protocols.tsx
  • src/app/__tests__/onboarding.test.tsx
  • src/components/contacts/contact-details-sheet.tsx
  • src/app/login/login-form.tsx
  • src/app/(app)/home/units.tsx
  • src/components/calls/dispatch-selection-modal.tsx
  • src/components/units/unit-details-sheet.tsx
  • src/app/(app)/settings.tsx
  • src/app/(app)/__tests__/protocols.test.tsx
  • src/stores/auth/store.tsx
  • src/components/personnel/personnel-details-sheet.tsx
  • src/app/(app)/__tests__/settings.test.tsx
  • src/components/protocols/protocol-details-sheet.tsx
  • src/components/status/personnel-status-bottom-sheet.tsx
  • src/components/units/unit-card.tsx
  • src/components/messages/__tests__/message-details-sheet.test.tsx
  • src/components/calls/__tests__/close-call-bottom-sheet.test.tsx
  • src/components/protocols/protocol-card.tsx
  • src/components/calls/close-call-bottom-sheet.tsx
src/**

📄 CodeRabbit inference engine (.cursorrules)

src/**: Organize files by feature, grouping related components, hooks, and styles
Directory and file names should be lowercase and hyphenated (e.g., user-profile)

Files:

  • src/components/maps/full-screen-location-picker.tsx
  • src/components/maps/location-picker.tsx
  • src/app/(app)/protocols.tsx
  • src/app/__tests__/onboarding.test.tsx
  • src/translations/en.json
  • src/components/contacts/contact-details-sheet.tsx
  • src/app/login/login-form.tsx
  • src/models/v4/callProtocols/callProtocolsResultData.ts
  • src/app/(app)/home/units.tsx
  • src/components/calls/dispatch-selection-modal.tsx
  • src/translations/es.json
  • src/components/units/unit-details-sheet.tsx
  • src/translations/ar.json
  • src/app/(app)/settings.tsx
  • src/stores/app/__tests__/core-store.test.ts
  • src/app/(app)/__tests__/protocols.test.tsx
  • src/lib/navigation.ts
  • src/lib/storage/__tests__/clear-all-data.test.ts
  • src/stores/auth/__tests__/store-token-refresh.test.ts
  • src/stores/auth/store.tsx
  • src/lib/__tests__/navigation.test.ts
  • src/stores/protocols/__tests__/store.test.ts
  • src/lib/storage/clear-all-data.ts
  • src/components/personnel/personnel-details-sheet.tsx
  • src/app/(app)/__tests__/settings.test.tsx
  • src/components/protocols/protocol-details-sheet.tsx
  • src/stores/units/store.ts
  • src/components/status/personnel-status-bottom-sheet.tsx
  • src/components/units/unit-card.tsx
  • src/components/messages/__tests__/message-details-sheet.test.tsx
  • src/components/calls/__tests__/close-call-bottom-sheet.test.tsx
  • src/components/protocols/protocol-card.tsx
  • src/components/calls/close-call-bottom-sheet.tsx
  • src/stores/units/__tests__/store.test.ts
**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{test,spec}.{ts,tsx}: Create Jest tests for all generated components, services, and logic
Ensure tests run without errors and fix failing tests

Files:

  • src/app/__tests__/onboarding.test.tsx
  • src/stores/app/__tests__/core-store.test.ts
  • src/app/(app)/__tests__/protocols.test.tsx
  • src/lib/storage/__tests__/clear-all-data.test.ts
  • src/stores/auth/__tests__/store-token-refresh.test.ts
  • src/lib/__tests__/navigation.test.ts
  • src/stores/protocols/__tests__/store.test.ts
  • src/app/(app)/__tests__/settings.test.tsx
  • src/components/messages/__tests__/message-details-sheet.test.tsx
  • src/components/calls/__tests__/close-call-bottom-sheet.test.tsx
  • src/stores/units/__tests__/store.test.ts
src/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.test.{ts,tsx}: Create and use Jest tests to validate all generated components
Generate tests for all components, services, and logic; ensure tests run without errors

Files:

  • src/app/__tests__/onboarding.test.tsx
  • src/stores/app/__tests__/core-store.test.ts
  • src/app/(app)/__tests__/protocols.test.tsx
  • src/lib/storage/__tests__/clear-all-data.test.ts
  • src/stores/auth/__tests__/store-token-refresh.test.ts
  • src/lib/__tests__/navigation.test.ts
  • src/stores/protocols/__tests__/store.test.ts
  • src/app/(app)/__tests__/settings.test.tsx
  • src/components/messages/__tests__/message-details-sheet.test.tsx
  • src/components/calls/__tests__/close-call-bottom-sheet.test.tsx
  • src/stores/units/__tests__/store.test.ts
src/translations/**/*.json

📄 CodeRabbit inference engine (.cursorrules)

Store translation dictionary files under src/translations as JSON resources

Files:

  • src/translations/en.json
  • src/translations/es.json
  • src/translations/ar.json
src/translations/**/*.{ts,tsx,json}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Store translation dictionary files in src/translations

Files:

  • src/translations/en.json
  • src/translations/es.json
  • src/translations/ar.json
🧠 Learnings (31)
📚 Learning: 2025-08-21T02:47:24.081Z
Learnt from: CR
Repo: Resgrid/Responder PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-21T02:47:24.081Z
Learning: Applies to **/*.tsx : Use rnmapbox/maps for maps, mapping, or vehicle navigation

Applied to files:

  • src/components/maps/full-screen-location-picker.tsx
  • src/components/maps/location-picker.tsx
  • src/components/units/unit-details-sheet.tsx
  • src/app/(app)/settings.tsx
  • src/lib/navigation.ts
  • src/lib/__tests__/navigation.test.ts
📚 Learning: 2025-08-21T02:46:42.743Z
Learnt from: CR
Repo: Resgrid/Responder PR: 0
File: .cursorrules:0-0
Timestamp: 2025-08-21T02:46:42.743Z
Learning: Applies to **/*.tsx : Use rnmapbox/maps for maps or navigation features

Applied to files:

  • src/components/maps/full-screen-location-picker.tsx
  • src/components/maps/location-picker.tsx
  • src/components/units/unit-details-sheet.tsx
  • src/app/(app)/settings.tsx
  • src/lib/navigation.ts
  • src/lib/__tests__/navigation.test.ts
📚 Learning: 2025-08-21T02:46:42.743Z
Learnt from: CR
Repo: Resgrid/Responder PR: 0
File: .cursorrules:0-0
Timestamp: 2025-08-21T02:46:42.743Z
Learning: Applies to **/*.{test,spec}.{ts,tsx} : Ensure tests run without errors and fix failing tests

Applied to files:

  • src/app/__tests__/onboarding.test.tsx
  • jest.config.js
  • src/lib/storage/__tests__/clear-all-data.test.ts
  • src/stores/auth/__tests__/store-token-refresh.test.ts
📚 Learning: 2025-08-21T02:47:24.081Z
Learnt from: CR
Repo: Resgrid/Responder PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-21T02:47:24.081Z
Learning: Applies to src/**/*.test.{ts,tsx} : Create and use Jest tests to validate all generated components

Applied to files:

  • src/app/__tests__/onboarding.test.tsx
  • jest.config.js
  • src/lib/storage/__tests__/clear-all-data.test.ts
  • src/app/(app)/__tests__/settings.test.tsx
  • src/components/calls/__tests__/close-call-bottom-sheet.test.tsx
📚 Learning: 2025-08-21T02:47:24.081Z
Learnt from: CR
Repo: Resgrid/Responder PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-21T02:47:24.081Z
Learning: Applies to **/*.tsx : Ensure UI is intuitive, user-friendly, and works across different devices and screen sizes

Applied to files:

  • src/app/__tests__/onboarding.test.tsx
  • src/components/status/personnel-status-bottom-sheet.tsx
📚 Learning: 2025-08-21T02:47:24.081Z
Learnt from: CR
Repo: Resgrid/Responder PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-21T02:47:24.081Z
Learning: Applies to src/**/*.test.{ts,tsx} : Generate tests for all components, services, and logic; ensure tests run without errors

Applied to files:

  • src/app/__tests__/onboarding.test.tsx
  • src/lib/storage/__tests__/clear-all-data.test.ts
  • src/app/(app)/__tests__/settings.test.tsx
📚 Learning: 2025-08-21T02:46:42.743Z
Learnt from: CR
Repo: Resgrid/Responder PR: 0
File: .cursorrules:0-0
Timestamp: 2025-08-21T02:46:42.743Z
Learning: Applies to **/*.{test,spec}.{ts,tsx} : Create Jest tests for all generated components, services, and logic

Applied to files:

  • src/app/__tests__/onboarding.test.tsx
  • jest.config.js
  • src/lib/storage/__tests__/clear-all-data.test.ts
  • src/app/(app)/__tests__/settings.test.tsx
  • src/components/calls/__tests__/close-call-bottom-sheet.test.tsx
📚 Learning: 2025-08-21T02:47:24.081Z
Learnt from: CR
Repo: Resgrid/Responder PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-21T02:47:24.081Z
Learning: Applies to **/*.tsx : Ensure the app is accessible following WCAG guidelines for mobile

Applied to files:

  • src/app/__tests__/onboarding.test.tsx
📚 Learning: 2025-08-21T02:47:24.081Z
Learnt from: CR
Repo: Resgrid/Responder PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-21T02:47:24.081Z
Learning: Applies to **/*.tsx : Ensure all components are mobile-friendly, responsive, and support both iOS and Android

Applied to files:

  • src/app/__tests__/onboarding.test.tsx
  • src/components/status/personnel-status-bottom-sheet.tsx
📚 Learning: 2025-08-21T02:46:42.743Z
Learnt from: CR
Repo: Resgrid/Responder PR: 0
File: .cursorrules:0-0
Timestamp: 2025-08-21T02:46:42.743Z
Learning: Applies to **/*.tsx : Use lucide-react-native for icons directly; do not use the gluestack-ui icon component

Applied to files:

  • src/components/contacts/contact-details-sheet.tsx
  • package.json
  • src/components/personnel/personnel-details-sheet.tsx
📚 Learning: 2025-08-21T02:47:24.081Z
Learnt from: CR
Repo: Resgrid/Responder PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-21T02:47:24.081Z
Learning: Applies to **/*.tsx : Use lucide-react-native for icons directly in markup; do not use gluestack-ui icon component

Applied to files:

  • src/components/contacts/contact-details-sheet.tsx
  • package.json
  • src/components/personnel/personnel-details-sheet.tsx
📚 Learning: 2025-08-21T02:47:24.081Z
Learnt from: CR
Repo: Resgrid/Responder PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-21T02:47:24.081Z
Learning: Applies to src/components/ui/**/*.tsx : Use gluestack-ui components from components/ui; if unavailable, style via StyleSheet.create or styled-components

Applied to files:

  • src/components/contacts/contact-details-sheet.tsx
  • package.json
  • src/app/(app)/settings.tsx
  • src/components/personnel/personnel-details-sheet.tsx
  • src/components/status/personnel-status-bottom-sheet.tsx
  • src/components/calls/close-call-bottom-sheet.tsx
📚 Learning: 2025-08-21T02:47:24.081Z
Learnt from: CR
Repo: Resgrid/Responder PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-21T02:47:24.081Z
Learning: Use React Navigation for handling navigation and deep linking with best practices

Applied to files:

  • src/components/contacts/contact-details-sheet.tsx
  • src/components/units/unit-details-sheet.tsx
  • src/app/(app)/settings.tsx
  • src/components/status/personnel-status-bottom-sheet.tsx
  • src/components/calls/close-call-bottom-sheet.tsx
📚 Learning: 2025-08-21T02:46:42.743Z
Learnt from: CR
Repo: Resgrid/Responder PR: 0
File: .cursorrules:0-0
Timestamp: 2025-08-21T02:46:42.743Z
Learning: Applies to **/*.{ts,tsx} : Use React Navigation for navigation and deep linking following best practices

Applied to files:

  • src/components/contacts/contact-details-sheet.tsx
  • src/components/units/unit-details-sheet.tsx
  • src/app/(app)/settings.tsx
  • jest.config.js
  • src/lib/navigation.ts
  • src/lib/__tests__/navigation.test.ts
  • src/components/status/personnel-status-bottom-sheet.tsx
  • src/components/calls/close-call-bottom-sheet.tsx
📚 Learning: 2025-08-21T02:47:24.081Z
Learnt from: CR
Repo: Resgrid/Responder PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-21T02:47:24.081Z
Learning: Applies to **/*.tsx : Use react-hook-form for form handling

Applied to files:

  • src/app/login/login-form.tsx
📚 Learning: 2025-08-21T02:47:24.081Z
Learnt from: CR
Repo: Resgrid/Responder PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-21T02:47:24.081Z
Learning: Applies to **/*.tsx : Utilize React.FC for defining functional components with props

Applied to files:

  • src/app/login/login-form.tsx
📚 Learning: 2025-08-21T02:46:42.743Z
Learnt from: CR
Repo: Resgrid/Responder PR: 0
File: .cursorrules:0-0
Timestamp: 2025-08-21T02:46:42.743Z
Learning: Applies to **/*.{ts,tsx} : Use react-hook-form for form handling

Applied to files:

  • src/app/login/login-form.tsx
📚 Learning: 2025-08-21T02:46:42.743Z
Learnt from: CR
Repo: Resgrid/Responder PR: 0
File: .cursorrules:0-0
Timestamp: 2025-08-21T02:46:42.743Z
Learning: Applies to **/*.{ts,tsx} : Use zustand for state management

Applied to files:

  • src/app/(app)/home/units.tsx
  • src/stores/auth/store.tsx
  • src/stores/units/store.ts
📚 Learning: 2025-08-21T02:47:24.081Z
Learnt from: CR
Repo: Resgrid/Responder PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-21T02:47:24.081Z
Learning: Applies to **/*.tsx : Use functional components and hooks over class components

Applied to files:

  • src/components/calls/dispatch-selection-modal.tsx
📚 Learning: 2025-08-21T02:46:42.743Z
Learnt from: CR
Repo: Resgrid/Responder PR: 0
File: .cursorrules:0-0
Timestamp: 2025-08-21T02:46:42.743Z
Learning: Applies to **/*.tsx : Use functional components and React hooks instead of class components

Applied to files:

  • src/components/calls/dispatch-selection-modal.tsx
📚 Learning: 2025-08-21T02:47:24.081Z
Learnt from: CR
Repo: Resgrid/Responder PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-21T02:47:24.081Z
Learning: Applies to **/*.tsx : Ensure components are modular, reusable, and maintainable

Applied to files:

  • src/components/calls/dispatch-selection-modal.tsx
📚 Learning: 2025-08-21T02:46:42.743Z
Learnt from: CR
Repo: Resgrid/Responder PR: 0
File: .cursorrules:0-0
Timestamp: 2025-08-21T02:46:42.743Z
Learning: Applies to **/*.tsx : Use gluestack-ui for styling where available from components/ui; otherwise, style via StyleSheet.create or styled-components

Applied to files:

  • package.json
  • src/app/(app)/settings.tsx
  • src/components/calls/close-call-bottom-sheet.tsx
📚 Learning: 2025-08-21T02:46:42.743Z
Learnt from: CR
Repo: Resgrid/Responder PR: 0
File: .cursorrules:0-0
Timestamp: 2025-08-21T02:46:42.743Z
Learning: Applies to **/*.{ts,tsx} : Use react-native-mmkv for local storage

Applied to files:

  • jest.config.js
  • src/stores/auth/store.tsx
  • src/lib/storage/clear-all-data.ts
  • src/components/status/personnel-status-bottom-sheet.tsx
📚 Learning: 2025-08-21T02:47:24.081Z
Learnt from: CR
Repo: Resgrid/Responder PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-21T02:47:24.081Z
Learning: Applies to **/*.tsx : Optimize image handling using react-native-fast-image

Applied to files:

  • jest.config.js
📚 Learning: 2025-08-21T02:47:24.081Z
Learnt from: CR
Repo: Resgrid/Responder PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-21T02:47:24.081Z
Learning: Applies to **/*.{ts,tsx} : Use react-i18next for internationalization

Applied to files:

  • jest.config.js
  • src/components/units/unit-card.tsx
📚 Learning: 2025-08-21T02:46:42.743Z
Learnt from: CR
Repo: Resgrid/Responder PR: 0
File: .cursorrules:0-0
Timestamp: 2025-08-21T02:46:42.743Z
Learning: Applies to **/*.tsx : Use react-native-fast-image for image handling instead of the default Image where appropriate

Applied to files:

  • jest.config.js
📚 Learning: 2025-08-21T02:47:24.081Z
Learnt from: CR
Repo: Resgrid/Responder PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-21T02:47:24.081Z
Learning: Use zustand for state management

Applied to files:

  • src/stores/auth/store.tsx
  • src/stores/units/store.ts
📚 Learning: 2025-08-21T02:47:24.081Z
Learnt from: CR
Repo: Resgrid/Responder PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-21T02:47:24.081Z
Learning: Applies to **/*.{ts,tsx} : Use axios for API requests

Applied to files:

  • src/stores/auth/store.tsx
📚 Learning: 2025-08-21T02:46:42.743Z
Learnt from: CR
Repo: Resgrid/Responder PR: 0
File: .cursorrules:0-0
Timestamp: 2025-08-21T02:46:42.743Z
Learning: Applies to **/*.{ts,tsx} : Use Expo SecureStore for sensitive data storage

Applied to files:

  • src/stores/auth/store.tsx
  • src/lib/storage/clear-all-data.ts
📚 Learning: 2025-08-21T02:46:42.743Z
Learnt from: CR
Repo: Resgrid/Responder PR: 0
File: .cursorrules:0-0
Timestamp: 2025-08-21T02:46:42.743Z
Learning: Applies to **/*.tsx : Wrap all user-facing text in t() from react-i18next for translations

Applied to files:

  • src/components/units/unit-card.tsx
📚 Learning: 2025-08-21T02:46:42.743Z
Learnt from: CR
Repo: Resgrid/Responder PR: 0
File: .cursorrules:0-0
Timestamp: 2025-08-21T02:46:42.743Z
Learning: Applies to **/*.tsx : Optimize FlatList with removeClippedSubviews, maxToRenderPerBatch, and windowSize

Applied to files:

  • src/components/calls/close-call-bottom-sheet.tsx
🧬 Code graph analysis (14)
src/app/__tests__/onboarding.test.tsx (1)
src/app/onboarding.tsx (1)
  • Onboarding (119-291)
src/app/(app)/home/units.tsx (2)
src/stores/units/store.ts (1)
  • useUnitsStore (45-143)
src/components/units/unit-card.tsx (1)
  • UnitCard (81-160)
src/components/units/unit-details-sheet.tsx (2)
src/services/analytics.service.ts (1)
  • trackEvent (97-134)
src/lib/navigation.ts (1)
  • openMapsWithDirections (68-158)
src/stores/app/__tests__/core-store.test.ts (1)
src/stores/app/core-store.ts (1)
  • useCoreStore (48-214)
src/lib/navigation.ts (1)
__mocks__/react-native.ts (2)
  • Platform (2-5)
  • Linking (32-35)
src/lib/storage/__tests__/clear-all-data.test.ts (1)
src/lib/storage/clear-all-data.ts (3)
  • registerStoreReset (29-35)
  • unregisterStoreReset (40-42)
  • clearAllAppData (132-178)
src/lib/__tests__/navigation.test.ts (1)
src/lib/navigation.ts (1)
  • openMapsWithDirections (68-158)
src/lib/storage/clear-all-data.ts (4)
src/lib/storage/index.tsx (1)
  • storage (5-5)
src/lib/storage/units-filter.ts (1)
  • clearUnitsFilterOptions (48-60)
src/lib/storage/personnel-filter.ts (1)
  • clearPersonnelFilterOptions (50-62)
src/lib/storage/secure-storage.ts (1)
  • clearSecureKeys (294-315)
src/components/personnel/personnel-details-sheet.tsx (1)
src/lib/utils.ts (2)
  • formatDateForDisplay (159-250)
  • parseDateISOString (138-146)
src/stores/units/store.ts (2)
src/lib/storage/secure-storage.ts (1)
  • set (118-130)
src/api/satuses/index.ts (1)
  • getAllUnitStatuses (21-24)
src/components/status/personnel-status-bottom-sheet.tsx (4)
__mocks__/@/components/ui/hstack.tsx (1)
  • HStack (4-6)
__mocks__/@/components/ui/button.tsx (2)
  • Button (4-6)
  • ButtonText (8-10)
__mocks__/react-native.ts (1)
  • Platform (2-5)
__mocks__/@/components/ui/vstack.tsx (1)
  • VStack (4-6)
src/components/calls/__tests__/close-call-bottom-sheet.test.tsx (2)
__mocks__/react-native.ts (2)
  • View (45-45)
  • Text (46-46)
src/components/ui/index.tsx (1)
  • TouchableOpacity (19-19)
src/components/protocols/protocol-card.tsx (1)
__mocks__/@/components/ui/pressable.tsx (1)
  • Pressable (4-6)
src/components/calls/close-call-bottom-sheet.tsx (5)
__mocks__/react-native.ts (2)
  • useColorScheme (37-37)
  • Platform (2-5)
__mocks__/@/components/ui/vstack.tsx (1)
  • VStack (4-6)
src/components/ui/form-control/index.tsx (3)
  • FormControl (412-412)
  • FormControlLabel (412-412)
  • FormControlLabelText (412-412)
__mocks__/@/components/ui/hstack.tsx (1)
  • HStack (4-6)
__mocks__/@/components/ui/button.tsx (2)
  • Button (4-6)
  • ButtonText (8-10)
🪛 Biome (2.1.2)
src/components/contacts/contact-details-sheet.tsx

[error] 96-96: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 97-97: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +275 to +288
jest.mock('@/components/ui/button', () => ({
Button: ({ children, onPress, disabled, testID }: any) => {
const { TouchableOpacity } = require('react-native');
return (
<TouchableOpacity testID={testID} onPress={onPress} disabled={disabled}>
{children}
</TouchableOpacity>
);
},
ButtonText: ({ children }: any) => {
const { Text } = require('react-native');
return <Text>{children}</Text>;
},
}));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, locate and examine the test file
fd "settings.test.tsx" --type f

Repository: Resgrid/Responder

Length of output: 102


🏁 Script executed:

# Read the test file to understand context
wc -l src/app/\(app\)/__tests__/settings.test.tsx

Repository: Resgrid/Responder

Length of output: 106


🏁 Script executed:

# Read the Button mock and surrounding test code
sed -n '270,320p' src/app/\(app\)/__tests__/settings.test.tsx

Repository: Resgrid/Responder

Length of output: 1508


🏁 Script executed:

# Search for fireEvent.press calls to understand how tests interact with Button
rg "fireEvent\.press|getByText.*ButtonText" src/app/\(app\)/__tests__/settings.test.tsx -A 2 -B 2

Repository: Resgrid/Responder

Length of output: 1881


🏁 Script executed:

# Find the actual Button component to understand expected usage
fd "button" --type f -e tsx -e ts | grep -E "components/ui"

Repository: Resgrid/Responder

Length of output: 97


🏁 Script executed:

# Read the actual Button component implementation
cat -n src/components/ui/button.tsx

Repository: Resgrid/Responder

Length of output: 121


🏁 Script executed:

# Search for how Button and ButtonText are used together in production code
rg "ButtonText" src --type tsx -B 2 -A 1 | head -50

Repository: Resgrid/Responder

Length of output: 88


🏁 Script executed:

# Search for where Button is defined or imported
rg "export.*Button|from.*button" src --type ts --type tsx -l

Repository: Resgrid/Responder

Length of output: 88


🏁 Script executed:

# Check if Button comes from gluestack-ui directly
rg "from.*gluestack-ui|import.*Button" src --type ts --type tsx -A 1 | head -30

Repository: Resgrid/Responder

Length of output: 88


🏁 Script executed:

# Look for the components/ui directory structure
fd --type f "button|Button" src/components --type f

Repository: Resgrid/Responder

Length of output: 192


🏁 Script executed:

# Check what's in components/ui
fd --type f . src/components/ui 2>/dev/null | head -20

Repository: Resgrid/Responder

Length of output: 850


🏁 Script executed:

cat -n src/components/ui/button/index.tsx

Repository: Resgrid/Responder

Length of output: 13940


🏁 Script executed:

# Check if there are other button-related files
ls -la src/components/ui/button/

Repository: Resgrid/Responder

Length of output: 237


🏁 Script executed:

# Check if there are other Button mock usages in test files to see patterns
find . -name "*.test.tsx" -o -name "*.spec.tsx" | head -20

Repository: Resgrid/Responder

Length of output: 998


🏁 Script executed:

# Look for __mocks__ button file
cat -n __mocks__/@/components/ui/button.tsx 2>/dev/null || echo "Mock not found at expected location"

Repository: Resgrid/Responder

Length of output: 403


🏁 Script executed:

# Search for ButtonText usage in actual component code (non-test)
rg "ButtonText" src --type ts -g "!*.test.tsx" -g "!*.spec.tsx" -B 1 -A 1 | head -40

Repository: Resgrid/Responder

Length of output: 3355


🏁 Script executed:

# Run the test to see if it actually passes or fails
cd src/app/\(app\)/__tests__ && npm test -- settings.test.tsx 2>&1 | tail -50 || echo "Test execution note: tests may not run in sandbox"

Repository: Resgrid/Responder

Length of output: 179


Button mock's ButtonText doesn't accept or forward props.
The mock ButtonText only accepts {children} while production ButtonText spreads {...props}. Tests press ButtonText via getByText, and without forwarding onPress, handlers may not trigger reliably. Spread props in ButtonText to match production behavior and ensure test reliability.

🤖 Prompt for AI Agents
In `@src/app/`(app)/__tests__/settings.test.tsx around lines 275 - 288, The mocked
ButtonText component in the test only accepts {children} so it doesn't forward
props (like onPress) used in production; update the mock for ButtonText to
accept (props) and spread {...props} onto the rendered Text (preserving
children) so event handlers and test queries (e.g., getByText) behave like the
real ButtonText; ensure the mock still returns a Text element and keep the
Button mock using TouchableOpacity as-is.

Comment on lines +226 to +231
<Button variant="outline" action="secondary" onPress={handleLogoutCancel} disabled={isLoggingOut} className="mr-3">
<ButtonText>{t('settings.logout_confirm_cancel')}</ButtonText>
</Button>
<Button action="negative" onPress={handleLogoutConfirm} disabled={isLoggingOut}>
<ButtonText>{isLoggingOut ? '...' : t('settings.logout_confirm_yes')}</ButtonText>
</Button>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Check if the file exists and read the relevant section
head -250 "src/app/(app)/settings.tsx" | tail -30

Repository: Resgrid/Responder

Length of output: 1368


🏁 Script executed:

# Check for any translation files and existing logout-related keys
fd -t f -e json -e ts -e tsx | grep -i "translation\|locale\|i18n" | head -20

Repository: Resgrid/Responder

Length of output: 261


🏁 Script executed:

# Search for existing logout confirmation translation keys
rg "logout_confirm" --type ts --type tsx

Repository: Resgrid/Responder

Length of output: 88


🏁 Script executed:

# Check imports and i18n setup in the settings file
head -50 "src/app/(app)/settings.tsx"

Repository: Resgrid/Responder

Length of output: 2750


🏁 Script executed:

# Check the English translation file for logout confirmation keys
cat "src/translations/en.json" | grep -A 20 "logout_confirm"

Repository: Resgrid/Responder

Length of output: 1409


🏁 Script executed:

# Get the full logout_confirm section from translation file
rg "logout_confirm" "src/translations/en.json" -A 3 -B 1

Repository: Resgrid/Responder

Length of output: 463


🏁 Script executed:

# Check the full structure of settings keys in en.json
python3 << 'EOF'
import json
with open('src/translations/en.json', 'r') as f:
    data = json.load(f)
    if 'settings' in data:
        settings = data['settings']
        logout_keys = {k: v for k, v in settings.items() if 'logout' in k}
        print("Logout-related keys found:")
        for key, value in sorted(logout_keys.items()):
            print(f"  {key}: {value}")
    else:
        print("No 'settings' key found in translation file")
EOF

Repository: Resgrid/Responder

Length of output: 147


🏁 Script executed:

# Search for any existing loading or status indicator keys in settings
rg "loading\|loading_state\|please_wait" "src/translations/en.json" | head -20

Repository: Resgrid/Responder

Length of output: 43


🏁 Script executed:

# Double-check the exact code in the file around line 226-231
sed -n '220,235p' "src/app/(app)/settings.tsx"

Repository: Resgrid/Responder

Length of output: 874


Localize the logout loading indicator.
The literal '...' is user-facing and bypasses i18n. Per coding guidelines, wrap it in t() and add a corresponding translation key.

Suggested change
-              <ButtonText>{isLoggingOut ? '...' : t('settings.logout_confirm_yes')}</ButtonText>
+              <ButtonText>{isLoggingOut ? t('settings.logout_confirm_loading') : t('settings.logout_confirm_yes')}</ButtonText>

Then add the key "logout_confirm_loading" to all translation files (en.json, es.json, ar.json).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Button variant="outline" action="secondary" onPress={handleLogoutCancel} disabled={isLoggingOut} className="mr-3">
<ButtonText>{t('settings.logout_confirm_cancel')}</ButtonText>
</Button>
<Button action="negative" onPress={handleLogoutConfirm} disabled={isLoggingOut}>
<ButtonText>{isLoggingOut ? '...' : t('settings.logout_confirm_yes')}</ButtonText>
</Button>
<Button variant="outline" action="secondary" onPress={handleLogoutCancel} disabled={isLoggingOut} className="mr-3">
<ButtonText>{t('settings.logout_confirm_cancel')}</ButtonText>
</Button>
<Button action="negative" onPress={handleLogoutConfirm} disabled={isLoggingOut}>
<ButtonText>{isLoggingOut ? t('settings.logout_confirm_loading') : t('settings.logout_confirm_yes')}</ButtonText>
</Button>
🤖 Prompt for AI Agents
In `@src/app/`(app)/settings.tsx around lines 226 - 231, The logout confirmation
uses a hardcoded loading label '...' which bypasses i18n; update the ButtonText
inside the Button that renders isLoggingOut to call
t('settings.logout_confirm_loading') instead of the literal, add the new
"logout_confirm_loading" key to all translation JSONs (e.g., en.json, es.json,
ar.json) and ensure translations are provided, keeping existing keys like
'settings.logout_confirm_yes' and functions such as handleLogoutConfirm and
ButtonText unchanged.

Comment on lines +134 to 136
// Navigate back to the calls list and refresh it
router.replace('/(app)/home/calls');
await fetchCalls();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Misleading error message when fetchCalls fails after successful close.

If closeCall succeeds but fetchCalls throws, the catch block shows call_detail.close_call_error, which is misleading since the call was actually closed successfully. Consider handling the fetchCalls error separately or silently, since the primary operation succeeded.

🐛 Proposed fix
       // Close the bottom sheet
       handleClose();

       // Navigate back to the calls list and refresh it
       router.replace('/(app)/home/calls');
-      await fetchCalls();
+      
+      // Refresh calls list - errors are non-critical since close succeeded
+      try {
+        await fetchCalls();
+      } catch (refreshError) {
+        console.warn('Failed to refresh calls list:', refreshError);
+      }
     } catch (error) {
       console.error('Error closing call:', error);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Navigate back to the calls list and refresh it
router.replace('/(app)/home/calls');
await fetchCalls();
// Navigate back to the calls list and refresh it
router.replace('/(app)/home/calls');
// Refresh calls list - errors are non-critical since close succeeded
try {
await fetchCalls();
} catch (refreshError) {
console.warn('Failed to refresh calls list:', refreshError);
}
🤖 Prompt for AI Agents
In `@src/components/calls/close-call-bottom-sheet.tsx` around lines 134 - 136, The
current catch shows call_detail.close_call_error even when closeCall succeeds
but fetchCalls fails; update the flow in the function that calls closeCall and
fetchCalls so closeCall is awaited and its errors are handled with the existing
close_call_error message, then run router.replace('/(app)/home/calls') and await
fetchCalls in a separate try/catch; if fetchCalls fails, either swallow/log the
error or surface a different message (e.g., call_detail.fetch_calls_error) but
do not show call_detail.close_call_error when closeCall succeeded—refer to the
closeCall call, the fetchCalls invocation, and the router.replace call to locate
where to split and handle errors independently.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/calls/call-notes-modal.tsx (1)

42-58: Analytics may fire multiple times while modal is open.

The dependency array includes callNotes?.length, isNotesLoading, and searchQuery, which will cause the call_notes_modal_viewed event to re-fire whenever these values change while the modal is open. If the intent is to track only once when the modal opens, consider separating the open-tracking logic:

Proposed fix
   useEffect(() => {
-    if (isOpen) {
+    if (isOpen && !wasModalOpenRef.current) {
       wasModalOpenRef.current = true;
       try {
         trackEvent('call_notes_modal_viewed', {
           timestamp: new Date().toISOString(),
           callId,
           noteCount: callNotes?.length || 0,
           hasNotes: Boolean(callNotes?.length),
           isLoading: isNotesLoading,
           hasSearchQuery: searchQuery.trim().length > 0,
         });
       } catch (error) {
         console.warn('Failed to track call notes modal analytics:', error);
       }
     }
   }, [isOpen, trackEvent, callId, callNotes?.length, isNotesLoading, searchQuery]);
🤖 Fix all issues with AI agents
In `@src/components/calls/call-images-modal.tsx`:
- Around line 87-97: The useEffect cleanup captures a stale isOpen and is
therefore unreliable; remove the cleanup function from the effect that currently
returns () => { if (!isOpen) { clearCallImages(); setImageErrors(new Set());
setFullScreenImage(null); setSelectedImageInfo(null); } } and rely on the
existing handleClose implementation (handleClose) which already performs the
comprehensive cleanup when the modal closes; ensure the useEffect keeps its
data-fetching logic (fetchCallImages) but no longer returns that cleanup
closure.

In `@src/components/calls/call-notes-modal.tsx`:
- Around line 157-158: The empty-state string "No notes found" is not localized;
update ListEmptyComponent to use the i18n t() function (e.g., obtain t from
useTranslation()) and pass the translated string to ZeroState: replace the raw
literal with heading={t('No notes found')} and include t in the useCallback
dependency array; ensure useTranslation is imported and called in the component
so t is available.
- Around line 175-177: The SearchIcon inside InputSlot in call-notes-modal.tsx
is using className which lucide-react-native ignores; update the SearchIcon
usage to pass a color prop instead of className and set size via the existing
size prop; for dark-mode support read the current color scheme (e.g., via
useColorScheme or your theme hook used elsewhere) and pass a conditional color
value to SearchIcon so it renders correctly in light and dark themes.
- Around line 222-247: The StyleSheet created by StyleSheet.create (the styles
object with keys container, header, footer, etc.) hardcodes light-mode colors
causing inconsistency with className-based dark-mode styling; update the
component to use React Native's useColorScheme (import and call it inside the
component) and compute dynamic colors (or a stylesFactory function) that return
theme-appropriate values for styles.container, styles.header, styles.footer,
listContent, and listContainer, then replace the static styles reference with
the theme-aware styles so the modal adapts to dark mode (alternatively switch
these specific elements to use className-driven styling consistent with the rest
of the JSX).
♻️ Duplicate comments (1)
src/components/calls/close-call-bottom-sheet.tsx (1)

132-134: Misleading error message when fetchCalls fails after successful close.

The navigation change to router.replace is appropriate. However, the previously flagged issue remains: if closeCall succeeds but fetchCalls throws on Line 134, the catch block shows call_detail.close_call_error, which is misleading since the call was actually closed successfully.

🐛 Proposed fix
       // Close the bottom sheet
       handleClose();

       // Navigate back to the calls list and refresh it
       router.replace('/(app)/home/calls');
-      await fetchCalls();
+      
+      // Refresh calls list - errors are non-critical since close succeeded
+      try {
+        await fetchCalls();
+      } catch (refreshError) {
+        console.warn('Failed to refresh calls list:', refreshError);
+      }
     } catch (error) {
       console.error('Error closing call:', error);
🧹 Nitpick comments (9)
src/components/contacts/contact-details-sheet.tsx (2)

115-118: Console logs should use plain English for debugging.

console.warn on line 116 uses t() for the log message. Developer logs are not user-facing and should remain in English for easier debugging and searchability in log aggregation tools. Only the Alert.alert calls need translations.

🔧 Proposed fix
     } catch (error) {
-      console.warn(t('contacts.openLinkFailed', { action: actionType }), error);
+      console.warn(`Failed to open ${actionType} link:`, error);
       Alert.alert(t('contacts.errorTitle'), t('contacts.openAppError', { action: actionType }));
     }

344-344: Consider whether fax numbers should be dialable.

The fax number field has actionType="phone", which will attempt to dial it. While technically valid, users might not expect to call a fax number from their mobile device. Consider either:

  • Removing actionType for fax (making it non-actionable)
  • Keeping it if users may want to dial fax machines via services that support it

This is a minor UX consideration.

src/components/calls/full-screen-image-modal.tsx (1)

1-1: Prefer react-native-fast-image to align with the repo standard.

Switching to expo-image diverges from the project guidance to use react-native-fast-image for image handling. If expo-image isn’t explicitly approved for this screen, consider migrating to FastImage (or add a note in the PR description explaining the exception). As per coding guidelines, prefer react-native-fast-image for images.

♻️ Proposed adjustment (if FastImage is preferred)
-import { Image } from 'expo-image';
+import FastImage from 'react-native-fast-image';

...

-            <Image
+            <FastImage
               source={imageSource}
               style={{
                 width: screenWidth,
                 height: screenHeight * 0.8, // Leave some space for safe areas
                 maxWidth: screenWidth,
                 maxHeight: screenHeight * 0.8,
               }}
-              contentFit="contain"
-              cachePolicy="memory-disk"
-              transition={200}
+              resizeMode={FastImage.resizeMode.contain}
               testID="full-screen-image"
             />

Also applies to: 154-164

src/components/calls/call-notes-modal.tsx (2)

89-94: Consider providing user feedback on error.

Per coding guidelines, errors should be handled gracefully with user feedback. Currently, the error is only logged to console.

Optional enhancement
       try {
         await addNote(callId, newNote, currentUser, null, null);
         setNewNote('');
       } catch (error) {
         console.error('Failed to add note:', error);
+        // Consider showing a toast or alert to the user
       }

188-196: Consider FlatList optimization props for better performance.

Per coding guidelines, optimize FlatList with removeClippedSubviews, maxToRenderPerBatch, and windowSize.

Suggested enhancement
             <FlatList
               data={filteredNotes}
               renderItem={renderNoteItem}
               keyExtractor={keyExtractor}
               keyboardShouldPersistTaps="handled"
               contentContainerStyle={styles.listContent}
               ListEmptyComponent={ListEmptyComponent}
               showsVerticalScrollIndicator={true}
+              removeClippedSubviews={true}
+              maxToRenderPerBatch={10}
+              windowSize={5}
             />
src/stores/units/store.ts (1)

61-70: Avoid blocking unit list on optional status fetch.
fetchUnitStatuses is described as optional, but Promise.all makes the unit list wait on it. Consider starting the status fetch in parallel and only awaiting the units response so the list can render sooner.

♻️ Proposed refactor
-      // Fetch units and unit statuses in parallel
-      const [unitsResponse] = await Promise.all([getUnitsInfos(filterString), fetchUnitStatuses()]);
-
-      set({ units: unitsResponse.Data || [], isLoading: false });
+      // Fetch units immediately; status fetch can complete in the background
+      const unitsPromise = getUnitsInfos(filterString);
+      void fetchUnitStatuses();
+      const unitsResponse = await unitsPromise;
+
+      set({ units: unitsResponse.Data || [], isLoading: false });
src/components/personnel/personnel-details-sheet.tsx (1)

165-189: Avoid double parsing/formatting per render.
safeFormatTimestamp is invoked twice for each timestamp (condition + text). Cache the formatted value once to reduce work and duplicate warnings.

♻️ Proposed refactor
   const fullName = `${selectedPersonnel.FirstName} ${selectedPersonnel.LastName}`.trim();
+  const statusTimestampText = safeFormatTimestamp(selectedPersonnel.StatusTimestamp, 'yyyy-MM-dd HH:mm Z');
+  const staffingTimestampText = safeFormatTimestamp(selectedPersonnel.StaffingTimestamp, 'yyyy-MM-dd HH:mm Z');

-                  {selectedPersonnel.StatusTimestamp && safeFormatTimestamp(selectedPersonnel.StatusTimestamp, 'yyyy-MM-dd HH:mm Z') ? (
+                  {statusTimestampText ? (
                     <HStack space="xs" className="items-center">
                       <Calendar size={16} className="text-gray-600 dark:text-gray-400" />
-                      <Text className="text-sm text-gray-600 dark:text-gray-400">{safeFormatTimestamp(selectedPersonnel.StatusTimestamp, 'yyyy-MM-dd HH:mm Z')}</Text>
+                      <Text className="text-sm text-gray-600 dark:text-gray-400">{statusTimestampText}</Text>
                     </HStack>
                   ) : null}

-                    {selectedPersonnel.StaffingTimestamp && safeFormatTimestamp(selectedPersonnel.StaffingTimestamp, 'yyyy-MM-dd HH:mm Z') ? (
+                    {staffingTimestampText ? (
                       <HStack space="xs" className="items-center">
                         <Calendar size={16} className="text-gray-600 dark:text-gray-400" />
-                        <Text className="text-sm text-gray-600 dark:text-gray-400">{safeFormatTimestamp(selectedPersonnel.StaffingTimestamp, 'yyyy-MM-dd HH:mm Z')}</Text>
+                        <Text className="text-sm text-gray-600 dark:text-gray-400">{staffingTimestampText}</Text>
                       </HStack>
                     ) : null}
src/components/personnel/__tests__/personnel-details-sheet.test.tsx (1)

82-85: Prefer partial mock for react-native to avoid brittle tests.
Overriding the entire module with only ScrollView can break other imports in the test environment. A partial mock keeps the rest of the API intact.

♻️ Proposed refactor
-jest.mock('react-native', () => ({
-  ScrollView: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
-}));
+jest.mock('react-native', () => {
+  const RN = jest.requireActual('react-native');
+  return {
+    ...RN,
+    ScrollView: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
+  };
+});
src/components/units/unit-details-sheet.tsx (1)

171-184: Add accessibility metadata to the new Pressable.
This is a new interactive region; adding role/label/hint improves screen‑reader usability.

♿ Proposed update
-                <Pressable onPress={handleOpenMaps} testID="location-press">
+                <Pressable
+                  onPress={handleOpenMaps}
+                  testID="location-press"
+                  accessibilityRole="button"
+                  accessibilityLabel={t('units.location')}
+                  accessibilityHint={t('units.tapToOpenMaps')}
+                >

As per coding guidelines, accessibility support is required.

@ucswift
Copy link
Member Author

ucswift commented Jan 17, 2026

Approve

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is approved.

@ucswift ucswift merged commit 9681f96 into master Jan 17, 2026
11 of 12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants