-
Notifications
You must be signed in to change notification settings - Fork 5
RR-T40 Logout fix, close call fix, units state fix, contacts fix, pro… #89
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Important Review skippedReview was skipped due to path filters ⛔ Files ignored due to path filters (1)
CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including You can disable this status message by setting the 📝 WalkthroughWalkthroughAdds 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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ 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. Comment |
There was a problem hiding this 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:errorshadows the prop.The
errorparameter in the catch block shadows theerrorprop from the component signature. Consider renaming toerrortrackingErrorfor 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: UnusedcolorSchemevariable.The
colorSchemeis destructured fromuseColorScheme()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 nativewindOr 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:handlersRecord is never used.The
handlersRecord is populated viauseEffectbut never read. TheonValueChangeis 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 usinguseCallbackfor the press handler.The inline anonymous function
() => onPress(protocol.ProtocolId)creates a new reference on each render. As per coding guidelines, wrap event handlers withuseCallbackto 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.warnis 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
handleOpenMapscallback correctly:
- Validates coordinates before proceeding
- Wraps analytics in try/catch to prevent UI breakage
- Handles navigation errors gracefully with
.catch()- Has correct dependencies in
useCallbackOne 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.
clearFilterOptionsdeletes specific MMKV keys, butclearMMKVStoragesubsequently callsstorage.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 whenclearStorageis 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: MemoizerenderItemto avoid new function per render.Inline renderItem functions can cause unnecessary re-renders in
FlashList. Consider extracting it withuseCallback. As per coding guidelines, avoid anonymous functions inrenderItem.♻️ 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
⛔ Files ignored due to path filters (1)
yarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (36)
jest.config.jspackage.jsonsrc/app/(app)/__tests__/protocols.test.tsxsrc/app/(app)/__tests__/settings.test.tsxsrc/app/(app)/home/units.tsxsrc/app/(app)/protocols.tsxsrc/app/(app)/settings.tsxsrc/app/__tests__/onboarding.test.tsxsrc/app/login/login-form.tsxsrc/components/calls/__tests__/close-call-bottom-sheet.test.tsxsrc/components/calls/close-call-bottom-sheet.tsxsrc/components/calls/dispatch-selection-modal.tsxsrc/components/contacts/contact-details-sheet.tsxsrc/components/maps/full-screen-location-picker.tsxsrc/components/maps/location-picker.tsxsrc/components/messages/__tests__/message-details-sheet.test.tsxsrc/components/personnel/personnel-details-sheet.tsxsrc/components/protocols/protocol-card.tsxsrc/components/protocols/protocol-details-sheet.tsxsrc/components/status/personnel-status-bottom-sheet.tsxsrc/components/units/unit-card.tsxsrc/components/units/unit-details-sheet.tsxsrc/lib/__tests__/navigation.test.tssrc/lib/navigation.tssrc/lib/storage/__tests__/clear-all-data.test.tssrc/lib/storage/clear-all-data.tssrc/models/v4/callProtocols/callProtocolsResultData.tssrc/stores/app/__tests__/core-store.test.tssrc/stores/auth/__tests__/store-token-refresh.test.tssrc/stores/auth/store.tsxsrc/stores/protocols/__tests__/store.test.tssrc/stores/units/__tests__/store.test.tssrc/stores/units/store.tssrc/translations/ar.jsonsrc/translations/en.jsonsrc/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.tsxsrc/components/maps/location-picker.tsxsrc/app/(app)/protocols.tsxsrc/app/__tests__/onboarding.test.tsxsrc/components/contacts/contact-details-sheet.tsxsrc/app/login/login-form.tsxsrc/models/v4/callProtocols/callProtocolsResultData.tssrc/app/(app)/home/units.tsxsrc/components/calls/dispatch-selection-modal.tsxsrc/components/units/unit-details-sheet.tsxsrc/app/(app)/settings.tsxsrc/stores/app/__tests__/core-store.test.tssrc/app/(app)/__tests__/protocols.test.tsxsrc/lib/navigation.tssrc/lib/storage/__tests__/clear-all-data.test.tssrc/stores/auth/__tests__/store-token-refresh.test.tssrc/stores/auth/store.tsxsrc/lib/__tests__/navigation.test.tssrc/stores/protocols/__tests__/store.test.tssrc/lib/storage/clear-all-data.tssrc/components/personnel/personnel-details-sheet.tsxsrc/app/(app)/__tests__/settings.test.tsxsrc/components/protocols/protocol-details-sheet.tsxsrc/stores/units/store.tssrc/components/status/personnel-status-bottom-sheet.tsxsrc/components/units/unit-card.tsxsrc/components/messages/__tests__/message-details-sheet.test.tsxsrc/components/calls/__tests__/close-call-bottom-sheet.test.tsxsrc/components/protocols/protocol-card.tsxsrc/components/calls/close-call-bottom-sheet.tsxsrc/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/mapsfor 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.tsxsrc/components/maps/location-picker.tsxsrc/app/(app)/protocols.tsxsrc/app/__tests__/onboarding.test.tsxsrc/components/contacts/contact-details-sheet.tsxsrc/app/login/login-form.tsxsrc/app/(app)/home/units.tsxsrc/components/calls/dispatch-selection-modal.tsxsrc/components/units/unit-details-sheet.tsxsrc/app/(app)/settings.tsxsrc/app/(app)/__tests__/protocols.test.tsxsrc/stores/auth/store.tsxsrc/components/personnel/personnel-details-sheet.tsxsrc/app/(app)/__tests__/settings.test.tsxsrc/components/protocols/protocol-details-sheet.tsxsrc/components/status/personnel-status-bottom-sheet.tsxsrc/components/units/unit-card.tsxsrc/components/messages/__tests__/message-details-sheet.test.tsxsrc/components/calls/__tests__/close-call-bottom-sheet.test.tsxsrc/components/protocols/protocol-card.tsxsrc/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.tsxsrc/components/maps/location-picker.tsxsrc/app/(app)/protocols.tsxsrc/app/__tests__/onboarding.test.tsxsrc/translations/en.jsonsrc/components/contacts/contact-details-sheet.tsxsrc/app/login/login-form.tsxsrc/models/v4/callProtocols/callProtocolsResultData.tssrc/app/(app)/home/units.tsxsrc/components/calls/dispatch-selection-modal.tsxsrc/translations/es.jsonsrc/components/units/unit-details-sheet.tsxsrc/translations/ar.jsonsrc/app/(app)/settings.tsxsrc/stores/app/__tests__/core-store.test.tssrc/app/(app)/__tests__/protocols.test.tsxsrc/lib/navigation.tssrc/lib/storage/__tests__/clear-all-data.test.tssrc/stores/auth/__tests__/store-token-refresh.test.tssrc/stores/auth/store.tsxsrc/lib/__tests__/navigation.test.tssrc/stores/protocols/__tests__/store.test.tssrc/lib/storage/clear-all-data.tssrc/components/personnel/personnel-details-sheet.tsxsrc/app/(app)/__tests__/settings.test.tsxsrc/components/protocols/protocol-details-sheet.tsxsrc/stores/units/store.tssrc/components/status/personnel-status-bottom-sheet.tsxsrc/components/units/unit-card.tsxsrc/components/messages/__tests__/message-details-sheet.test.tsxsrc/components/calls/__tests__/close-call-bottom-sheet.test.tsxsrc/components/protocols/protocol-card.tsxsrc/components/calls/close-call-bottom-sheet.tsxsrc/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.tsxsrc/stores/app/__tests__/core-store.test.tssrc/app/(app)/__tests__/protocols.test.tsxsrc/lib/storage/__tests__/clear-all-data.test.tssrc/stores/auth/__tests__/store-token-refresh.test.tssrc/lib/__tests__/navigation.test.tssrc/stores/protocols/__tests__/store.test.tssrc/app/(app)/__tests__/settings.test.tsxsrc/components/messages/__tests__/message-details-sheet.test.tsxsrc/components/calls/__tests__/close-call-bottom-sheet.test.tsxsrc/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.tsxsrc/stores/app/__tests__/core-store.test.tssrc/app/(app)/__tests__/protocols.test.tsxsrc/lib/storage/__tests__/clear-all-data.test.tssrc/stores/auth/__tests__/store-token-refresh.test.tssrc/lib/__tests__/navigation.test.tssrc/stores/protocols/__tests__/store.test.tssrc/app/(app)/__tests__/settings.test.tsxsrc/components/messages/__tests__/message-details-sheet.test.tsxsrc/components/calls/__tests__/close-call-bottom-sheet.test.tsxsrc/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.jsonsrc/translations/es.jsonsrc/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.jsonsrc/translations/es.jsonsrc/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.tsxsrc/components/maps/location-picker.tsxsrc/components/units/unit-details-sheet.tsxsrc/app/(app)/settings.tsxsrc/lib/navigation.tssrc/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.tsxsrc/components/maps/location-picker.tsxsrc/components/units/unit-details-sheet.tsxsrc/app/(app)/settings.tsxsrc/lib/navigation.tssrc/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.tsxjest.config.jssrc/lib/storage/__tests__/clear-all-data.test.tssrc/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.tsxjest.config.jssrc/lib/storage/__tests__/clear-all-data.test.tssrc/app/(app)/__tests__/settings.test.tsxsrc/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.tsxsrc/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.tsxsrc/lib/storage/__tests__/clear-all-data.test.tssrc/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.tsxjest.config.jssrc/lib/storage/__tests__/clear-all-data.test.tssrc/app/(app)/__tests__/settings.test.tsxsrc/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.tsxsrc/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.tsxpackage.jsonsrc/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.tsxpackage.jsonsrc/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.tsxpackage.jsonsrc/app/(app)/settings.tsxsrc/components/personnel/personnel-details-sheet.tsxsrc/components/status/personnel-status-bottom-sheet.tsxsrc/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.tsxsrc/components/units/unit-details-sheet.tsxsrc/app/(app)/settings.tsxsrc/components/status/personnel-status-bottom-sheet.tsxsrc/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.tsxsrc/components/units/unit-details-sheet.tsxsrc/app/(app)/settings.tsxjest.config.jssrc/lib/navigation.tssrc/lib/__tests__/navigation.test.tssrc/components/status/personnel-status-bottom-sheet.tsxsrc/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.tsxsrc/stores/auth/store.tsxsrc/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.jsonsrc/app/(app)/settings.tsxsrc/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.jssrc/stores/auth/store.tsxsrc/lib/storage/clear-all-data.tssrc/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.jssrc/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.tsxsrc/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.tsxsrc/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.
| 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>; | ||
| }, | ||
| })); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# First, locate and examine the test file
fd "settings.test.tsx" --type fRepository: Resgrid/Responder
Length of output: 102
🏁 Script executed:
# Read the test file to understand context
wc -l src/app/\(app\)/__tests__/settings.test.tsxRepository: 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.tsxRepository: 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 2Repository: 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.tsxRepository: 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 -50Repository: 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 -lRepository: 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 -30Repository: Resgrid/Responder
Length of output: 88
🏁 Script executed:
# Look for the components/ui directory structure
fd --type f "button|Button" src/components --type fRepository: Resgrid/Responder
Length of output: 192
🏁 Script executed:
# Check what's in components/ui
fd --type f . src/components/ui 2>/dev/null | head -20Repository: Resgrid/Responder
Length of output: 850
🏁 Script executed:
cat -n src/components/ui/button/index.tsxRepository: 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 -20Repository: 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 -40Repository: 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.
| <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> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# Check if the file exists and read the relevant section
head -250 "src/app/(app)/settings.tsx" | tail -30Repository: 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 -20Repository: Resgrid/Responder
Length of output: 261
🏁 Script executed:
# Search for existing logout confirmation translation keys
rg "logout_confirm" --type ts --type tsxRepository: 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 1Repository: 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")
EOFRepository: 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 -20Repository: 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.
| <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.
| // Navigate back to the calls list and refresh it | ||
| router.replace('/(app)/home/calls'); | ||
| await fetchCalls(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| // 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.
There was a problem hiding this 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, andsearchQuery, which will cause thecall_notes_modal_viewedevent 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 whenfetchCallsfails after successful close.The navigation change to
router.replaceis appropriate. However, the previously flagged issue remains: ifcloseCallsucceeds butfetchCallsthrows on Line 134, the catch block showscall_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.warnon line 116 usest()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 theAlert.alertcalls 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
actionTypefor 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: Preferreact-native-fast-imageto align with the repo standard.Switching to
expo-imagediverges from the project guidance to usereact-native-fast-imagefor image handling. Ifexpo-imageisn’t explicitly approved for this screen, consider migrating toFastImage(or add a note in the PR description explaining the exception). As per coding guidelines, preferreact-native-fast-imagefor 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, andwindowSize.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.
fetchUnitStatusesis described as optional, butPromise.allmakes 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.
safeFormatTimestampis 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 forreact-nativeto avoid brittle tests.
Overriding the entire module with onlyScrollViewcan 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.
|
Approve |
There was a problem hiding this 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.
…tocols fix.
Summary by CodeRabbit
New Features
Improvements
Stability
Tests
✏️ Tip: You can customize this high-level summary in your review settings.