Skip to content

RIC-T40 Fixes#18

Merged
ucswift merged 2 commits into
masterfrom
develop
Jul 23, 2026
Merged

RIC-T40 Fixes#18
ucswift merged 2 commits into
masterfrom
develop

Conversation

@ucswift

@ucswift ucswift commented Jul 23, 2026

Copy link
Copy Markdown
Member

PR Description

This PR delivers a set of fixes and improvements across the command board accountability system, offline status handling, toast notifications, and weather alert rendering.

Accountability & Check-in Timers

  • New interactive AccountabilitySection: Replaces the read-only server-computed accountability block with a self-contained component that lets the IC activate check-in timers, view personnel/unit/IC statuses, and perform check-ins directly from the command board.
  • Timer activation flow: Added toggleCallTimers API call (query-string based) and setCallTimersEnabled store action so ICs can enable timers on demand; the section shows an activation prompt when timers are inactive.
  • Personnel check-in statuses: New getCallPersonnelCheckInStatuses endpoint and store state with typed error handling (CallPersonnelCheckInStatusesFetchError), severity sorting, and polling integration.
  • IC check-in restriction: Removed the IC check-in type from the general check-in bottom sheet; it is now only available from the accountability section.
  • Managed personnel check-ins: performCheckIn supports a UserId field; these check-ins require a connection and cannot be queued offline.
  • Model corrections: UnitId changed from string to number | null; LastCheckIn made nullable.

Offline Status & Toast System

  • OfflineStatusToast replaces OfflineStatusBar: Network/pending-sync status is now surfaced as tappable, dismissable toasts instead of a layout bar, freeing vertical space above the navigator.
  • Toast enhancements: Toasts now support configurable duration, tap-to-act callbacks, manual dismiss (X button), and proper auto-dismiss timer cancellation.

Weather Alerts

  • Date parsing: Added parseWeatherAlertDate/formatWeatherAlertDate to correctly handle department-local timestamps (both 12-hour and 24-hour formats), eliminating "Invalid Date" display.
  • Map improvements: Detail map now supports MultiPolygon geometries via getPolygonBounds, validates center coordinates, and renders nothing when location data is unavailable instead of showing a default map.
  • Banner behavior: Dismissing now fires before navigation; close button properly stops event propagation with improved touch target.

App Shell & UI

  • Header layout: Added getAppHeaderHeight utility for consistent, safe-area-aware header sizing across portrait/landscape.
  • Touch targets: Drawer menu, back button, and notification button get hitSlop and reduced padding for better tap accessibility.

Project & Tooling

  • Renamed project references from "Resgrid Unit" to "Resgrid IC."
  • Added React Native best-practices and upgrade-workflow AI agent skill documentation.
  • Added /docs to .gitignore.

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@ucswift, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 19 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 15216ea5-2c0d-40e8-a6d1-992bbb8ecc75

📥 Commits

Reviewing files that changed from the base of the PR and between db80de2 and 38629f4.

⛔ Files ignored due to path filters (12)
  • .agents/skills/react-native-best-practices/references/images/bundle-treemap-source-map-explorer.png is excluded by !**/*.png
  • .agents/skills/react-native-best-practices/references/images/controlled-textinput-pingpong.png is excluded by !**/*.png
  • .agents/skills/react-native-best-practices/references/images/devtools-flamegraph.png is excluded by !**/*.png
  • .agents/skills/react-native-best-practices/references/images/emerge-xray-ios.png is excluded by !**/*.png
  • .agents/skills/react-native-best-practices/references/images/expo-atlas-treemap.png is excluded by !**/*.png
  • .agents/skills/react-native-best-practices/references/images/flashlight-flatlist-vs-flashlist.png is excluded by !**/*.png
  • .agents/skills/react-native-best-practices/references/images/fps-drop-graph.png is excluded by !**/*.png
  • .agents/skills/react-native-best-practices/references/images/memory-heap-snapshot.png is excluded by !**/*.png
  • .agents/skills/react-native-best-practices/references/images/tti-warm-start-diagram.png is excluded by !**/*.png
  • .agents/skills/react-native-best-practices/references/images/view-hierarchy-flattening.png is excluded by !**/*.png
  • .agents/skills/react-native-best-practices/references/images/xcode-instruments-templates.png is excluded by !**/*.png
  • .agents/skills/react-native-best-practices/references/images/xcode-thread-view.png is excluded by !**/*.png
📒 Files selected for processing (90)
  • .agents/skills/react-native-best-practices/POWER.md
  • .agents/skills/react-native-best-practices/SKILL.md
  • .agents/skills/react-native-best-practices/agents/openai.yaml
  • .agents/skills/react-native-best-practices/references/bundle-analyze-app.md
  • .agents/skills/react-native-best-practices/references/bundle-analyze-js.md
  • .agents/skills/react-native-best-practices/references/bundle-barrel-exports.md
  • .agents/skills/react-native-best-practices/references/bundle-code-splitting.md
  • .agents/skills/react-native-best-practices/references/bundle-hermes-mmap.md
  • .agents/skills/react-native-best-practices/references/bundle-library-size.md
  • .agents/skills/react-native-best-practices/references/bundle-native-assets.md
  • .agents/skills/react-native-best-practices/references/bundle-r8-android.md
  • .agents/skills/react-native-best-practices/references/bundle-tree-shaking.md
  • .agents/skills/react-native-best-practices/references/js-animations-reanimated.md
  • .agents/skills/react-native-best-practices/references/js-atomic-state.md
  • .agents/skills/react-native-best-practices/references/js-bottomsheet.md
  • .agents/skills/react-native-best-practices/references/js-concurrent-react.md
  • .agents/skills/react-native-best-practices/references/js-lists-flatlist-flashlist.md
  • .agents/skills/react-native-best-practices/references/js-measure-fps.md
  • .agents/skills/react-native-best-practices/references/js-memory-leaks.md
  • .agents/skills/react-native-best-practices/references/js-profile-react.md
  • .agents/skills/react-native-best-practices/references/js-react-compiler.md
  • .agents/skills/react-native-best-practices/references/js-uncontrolled-components.md
  • .agents/skills/react-native-best-practices/references/native-android-16kb-alignment.md
  • .agents/skills/react-native-best-practices/references/native-measure-tti.md
  • .agents/skills/react-native-best-practices/references/native-memory-leaks.md
  • .agents/skills/react-native-best-practices/references/native-memory-patterns.md
  • .agents/skills/react-native-best-practices/references/native-platform-setup.md
  • .agents/skills/react-native-best-practices/references/native-profiling.md
  • .agents/skills/react-native-best-practices/references/native-sdks-over-polyfills.md
  • .agents/skills/react-native-best-practices/references/native-threading-model.md
  • .agents/skills/react-native-best-practices/references/native-turbo-modules.md
  • .agents/skills/react-native-best-practices/references/native-view-flattening.md
  • .agents/skills/upgrading-react-native/SKILL.md
  • .agents/skills/upgrading-react-native/agents/openai.yaml
  • .agents/skills/upgrading-react-native/references/expo-sdk-upgrade.md
  • .agents/skills/upgrading-react-native/references/monorepo-singlerepo-targeting.md
  • .agents/skills/upgrading-react-native/references/react.md
  • .agents/skills/upgrading-react-native/references/upgrade-helper-core.md
  • .agents/skills/upgrading-react-native/references/upgrade-verification.md
  • .agents/skills/upgrading-react-native/references/upgrading-dependencies.md
  • .agents/skills/upgrading-react-native/references/upgrading-react-native.md
  • .gitignore
  • AGENTS.md
  • CLAUDE.md
  • src/api/check-in-timers/__tests__/check-in-timers.test.ts
  • src/api/check-in-timers/check-in-timers.ts
  • src/app/(app)/__tests__/command.test.tsx
  • src/app/(app)/_layout.tsx
  • src/app/(app)/command.tsx
  • src/app/weather-alert/[id].tsx
  • src/components/check-in-timers/__tests__/check-in-bottom-sheet.test.tsx
  • src/components/check-in-timers/__tests__/check-in-timer-card.test.tsx
  • src/components/check-in-timers/check-in-bottom-sheet.tsx
  • src/components/command/__tests__/accountability-section.test.tsx
  • src/components/command/__tests__/incident-weather-section.test.tsx
  • src/components/command/accountability-section.tsx
  • src/components/command/incident-weather-section.tsx
  • src/components/common/__tests__/offline-status-toast.test.tsx
  • src/components/common/offline-status-bar.tsx
  • src/components/common/offline-status-toast.tsx
  • src/components/notifications/NotificationButton.tsx
  • src/components/toast/__tests__/toast.test.tsx
  • src/components/toast/toast.tsx
  • src/components/weather-alerts/__tests__/weather-alert-banner.test.tsx
  • src/components/weather-alerts/__tests__/weather-alert-card.test.tsx
  • src/components/weather-alerts/__tests__/weather-alert-detail-map.test.tsx
  • src/components/weather-alerts/weather-alert-banner.tsx
  • src/components/weather-alerts/weather-alert-card.tsx
  • src/components/weather-alerts/weather-alert-detail-map.tsx
  • src/lib/__tests__/app-shell-layout.test.ts
  • src/lib/__tests__/weather-alert-utils.test.ts
  • src/lib/app-shell-layout.ts
  • src/lib/weather-alert-utils.ts
  • src/models/v4/checkIn/callPersonnelCheckInStatusResult.ts
  • src/models/v4/checkIn/callPersonnelCheckInStatusResultData.ts
  • src/models/v4/checkIn/checkInTimerStatusResultData.ts
  • src/models/v4/checkIn/resolvedCheckInTimerResultData.ts
  • src/stores/check-in-timers/__tests__/store.test.ts
  • src/stores/check-in-timers/store.ts
  • src/stores/toast/__tests__/store.test.ts
  • src/stores/toast/store.ts
  • src/translations/ar.json
  • src/translations/de.json
  • src/translations/en.json
  • src/translations/es.json
  • src/translations/fr.json
  • src/translations/it.json
  • src/translations/pl.json
  • src/translations/sv.json
  • src/translations/uk.json
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch develop

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.

@Resgrid-Bot

Resgrid-Bot commented Jul 23, 2026

Copy link
Copy Markdown

Code Review Could Not Complete ⚠️

The review failed before suggestions could be generated.

Reason: Transient error reaching the provider (openai_compatible). Try again.

After fixing the issue, comment @kody review on this PR to re-run the review.

Kody Guide: Usage and Configuration
Interacting with Kody
  • Request a Review: Ask Kody to review your PR manually by adding a comment with the @kody start-review command at the root of your PR.

  • Validate Business Logic: Ask Kody to validate your code against business rules by adding a comment with the @kody -v business-logic command.

  • Provide Feedback: Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

Current Kody Configuration
Review Options

The following review options are enabled or disabled:

Options Enabled
Bug
Performance
Security
Business Logic

Access your configuration settings here.

<View>
<Button
title="Load Feature"
onPress={() => setShowFeature(true)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Performance regression caused by inline arrow functions in JSX props creating new functions on every render. Move the function definitions outside the render method.

Kody rule violation: Avoid using .bind() or arrow functions in JSX props

Prompt for LLM

File .agents/skills/react-native-best-practices/references/bundle-code-splitting.md:

Line 150:

Performance regression caused by inline arrow functions in JSX props creating new functions on every render. Move the function definitions outside the render method.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

});

ScriptManager.shared.on('error', (scriptId, error) => {
console.error(`Failed: ${scriptId}`, error);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Unstructured error logging in console.error prevents searchable, parseable logs. Rule [3] requires using a structured logger with the operation name and relevant identifiers, such as logger.error('chunk_load_failed', { scriptId, error }).

Kody rule violation: Include error context in structured logs

Prompt for LLM

File .agents/skills/react-native-best-practices/references/bundle-code-splitting.md:

Line 231:

Unstructured error logging in `console.error` prevents searchable, parseable logs. Rule [3] requires using a structured logger with the operation name and relevant identifiers, such as `logger.error('chunk_load_failed', { scriptId, error })`.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.


```tsx
// Check if chunk loaded correctly
ScriptManager.shared.on('loading', (scriptId) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Event listener leak on ScriptManager.shared.on('loading') lacks a deterministic cleanup path. Rule [4] requires calling .off() or removeEventListener during component teardown to prevent dangling handlers.

Kody rule violation: Provide error handlers to subscription/listener APIs

Prompt for LLM

File .agents/skills/react-native-best-practices/references/bundle-code-splitting.md:

Line 222:

Event listener leak on `ScriptManager.shared.on('loading')` lacks a deterministic cleanup path. Rule [4] requires calling `.off()` or `removeEventListener` during component teardown to prevent dangling handlers.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.


```tsx
// Check if chunk loaded correctly
ScriptManager.shared.on('loading', (scriptId) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Memory leak caused by an unremoved event listener on ScriptManager. Rule [57] requires capturing the handler reference and calling ScriptManager.shared.off('loading', handler) during component teardown to prevent resource consumption.

Kody rule violation: Proper memory management in event listeners

Prompt for LLM

File .agents/skills/react-native-best-practices/references/bundle-code-splitting.md:

Line 222:

Memory leak caused by an unremoved event listener on `ScriptManager`. Rule [57] requires capturing the handler reference and calling `ScriptManager.shared.off('loading', handler)` during component teardown to prevent resource consumption.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

const TodoProvider = ({ children }) => {
const [state, setState] = useState({ filter: 'all', todos: [] });
return (
<TodoContext.Provider value={{ state, setState }}>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Unstable context value from the inline object literal {{ state, setState }} triggers unnecessary re-renders of all consumers on every render. Rule [55] requires wrapping the value with useMemo, such as const value = useMemo(() => ({ state, setState }), [state]).

Kody rule violation: React Context values should have stable identities

Prompt for LLM

File .agents/skills/react-native-best-practices/references/js-atomic-state.md:

Line 189:

Unstable context value from the inline object literal `{{ state, setState }}` triggers unnecessary re-renders of all consumers on every render. Rule [55] requires wrapping the value with `useMemo`, such as `const value = useMemo(() => ({ state, setState }), [state])`.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

const filter = get(filterAtom);
const todos = get(todosAtom);

if (filter === 'active') return todos.filter(t => !t.completed);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Magic strings used in comparisons risk silent failures from typos. Rule [9] requires defining a constants tuple, such as const FILTERS = ['all', 'active', 'completed'] as const; type Filter = typeof FILTERS[number];, to replace finite-set string literals.

Kody rule violation: Use enums instead of magic strings

Prompt for LLM

File .agents/skills/react-native-best-practices/references/js-atomic-state.md:

Line 84:

Magic strings used in comparisons risk silent failures from typos. Rule [9] requires defining a constants tuple, such as `const FILTERS = ['all', 'active', 'completed'] as const; type Filter = typeof FILTERS[number];`, to replace finite-set string literals.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

const BadList = ({ items }) => (
<ScrollView>
{items.map((item, index) => (
<View key={index}>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Anti-pattern identified using array index as a React key (<View key={index}>), normalizing a practice the pitfalls section (line 237) warns against. Replace it with a stable unique identifier like key={item.id}.

Kody rule violation: Avoid array indexes as keys in React lists

Prompt for LLM

File .agents/skills/react-native-best-practices/references/js-lists-flatlist-flashlist.md:

Line 65:

Anti-pattern identified using array index as a React key (`<View key={index}>`), normalizing a practice the pitfalls section (line 237) warns against. Replace it with a stable unique identifier like `key={item.id}`.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

```tsx
import performance from 'react-native-performance';

export default function HomeScreen() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Default export for HomeScreen violates Rule [75], which requires named exports for clarity, maintainability, and refactoring safety. Change to export function HomeScreen() and update imports to import { HomeScreen } from './HomeScreen'.

Kody rule violation: Avoid default exports

Prompt for LLM

File .agents/skills/react-native-best-practices/references/native-measure-tti.md:

Line 173:

Default export for `HomeScreen` violates Rule [75], which requires named exports for clarity, maintainability, and refactoring safety. Change to `export function HomeScreen()` and update imports to `import { HomeScreen } from './HomeScreen'`.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Comment on lines +155 to +156
let result = self.compute()
resolve(result)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Unhandled rejection risk where self.compute() lacks error handling, causing the JavaScript caller to hang indefinitely if it throws. Invoke the existing reject: RCTPromiseRejectBlock parameter (line 151) within a do/catch block to ensure the promise settles cleanly.

Kody rule violation: Handle async operations with proper error handling

Prompt for LLM

File .agents/skills/react-native-best-practices/references/native-threading-model.md:

Line 155 to 156:

Unhandled rejection risk where `self.compute()` lacks error handling, causing the JavaScript caller to hang indefinitely if it throws. Invoke the existing `reject: RCTPromiseRejectBlock` parameter (line 151) within a `do/catch` block to ensure the promise settles cleanly.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.


const getEndpoint = (path: string): MockEndpoint => {
const callIndex = mockCreateApiEndpoint.mock.calls.findIndex(([endpoint]) => endpoint === path);
return mockCreateApiEndpoint.mock.results[callIndex].value as unknown as MockEndpoint;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Opaque TypeError risk when dereferencing mock.results[callIndex].value without a null guard. findIndex (line 24) returns -1 when the path is not found, making the element undefined; add a bounds check or use optional chaining like mock.results[callIndex]?.value.

Kody rule violation: Add null checks before accessing properties

Prompt for LLM

File src/api/check-in-timers/__tests__/check-in-timers.test.ts:

Line 25:

Opaque `TypeError` risk when dereferencing `mock.results[callIndex].value` without a null guard. `findIndex` (line 24) returns -1 when the path is not found, making the element `undefined`; add a bounds check or use optional chaining like `mock.results[callIndex]?.value`.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.


const getEndpoint = (path: string): MockEndpoint => {
const callIndex = mockCreateApiEndpoint.mock.calls.findIndex(([endpoint]) => endpoint === path);
return mockCreateApiEndpoint.mock.results[callIndex].value as unknown as MockEndpoint;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Null pointer dereference risk on mock.results[callIndex].value when callIndex is -1 from findIndex. Guard the access with a null check or throw an explicit Error before accessing .value.

Kody rule violation: Add null checks to prevent NullReferenceException

Prompt for LLM

File src/api/check-in-timers/__tests__/check-in-timers.test.ts:

Line 25:

Null pointer dereference risk on `mock.results[callIndex].value` when `callIndex` is -1 from `findIndex`. Guard the access with a null check or throw an explicit `Error` before accessing `.value`.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.


const getEndpoint = (path: string): MockEndpoint => {
const callIndex = mockCreateApiEndpoint.mock.calls.findIndex(([endpoint]) => endpoint === path);
return mockCreateApiEndpoint.mock.results[callIndex].value as unknown as MockEndpoint;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Unvalidated array bounds when using callIndex (from findIndex) to index mock.results, yielding undefined when the endpoint path is not found. Validate the index before use, e.g., if (callIndex < 0 || callIndex >= mock.results.length) throw new Error(...).

Kody rule violation: Check query results before accessing indices

Prompt for LLM

File src/api/check-in-timers/__tests__/check-in-timers.test.ts:

Line 25:

Unvalidated array bounds when using `callIndex` (from `findIndex`) to index `mock.results`, yielding `undefined` when the endpoint path is not found. Validate the index before use, e.g., `if (callIndex < 0 || callIndex >= mock.results.length) throw new Error(...)`.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

CallId: callId,
Enabled: enabled,
export const getCallPersonnelCheckInStatuses = async (callId: number) => {
const response = await getCallPersonnelCheckInStatusesApi.get<CallPersonnelCheckInStatusResult>({

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Unhandled external HTTP call failure in getCallPersonnelCheckInStatusesApi.get violates Rule 28. Wrap the call in a try/catch block, include the callId and endpoint name in the error context, and map it to an application-level error.

Kody rule violation: Add try-catch blocks for external calls

Prompt for LLM

File src/api/check-in-timers/check-in-timers.ts:

Line 60:

Unhandled external HTTP call failure in `getCallPersonnelCheckInStatusesApi.get` violates Rule 28. Wrap the call in a `try/catch` block, include the `callId` and endpoint name in the error context, and map it to an application-level error.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Comment thread src/app/(app)/_layout.tsx
<Pressable
className="p-3"
className="p-2"
hitSlop={4}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Magic number 4 used directly as hitSlop degrades readability and maintainability across multiple components. Extract it into a named constant like const HIT_SLOP = 4; or a shared PressableHitSlop object.

Kody rule violation: Replace magic numbers with named constants

Prompt for LLM

File src/app/(app)/_layout.tsx:

Line 546:

Magic number `4` used directly as `hitSlop` degrades readability and maintainability across multiple components. Extract it into a named constant like `const HIT_SLOP = 4;` or a shared `PressableHitSlop` object.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

</VStack>
<VStack space="xs" className="items-end">
<Badge action={getParBadgeAction(entry.Status)} variant="solid">
<BadgeText className="text-white">{entry.Status === 'Green' ? t('command.par_green') : entry.Status === 'Warning' ? t('command.par_warning') : t('command.par_critical')}</BadgeText>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Duplicated magic strings for status values like 'Green' and 'Warning' introduce drift and typos. Centralize them into constants or an enum, such as const PAR_STATUS = { GREEN: 'Green', WARNING: 'Warning', CRITICAL: 'Critical' } as const, in a shared module.

Kody rule violation: Centralize string constants

Prompt for LLM

File src/components/command/accountability-section.tsx:

Line 191:

Duplicated magic strings for status values like `'Green'` and `'Warning'` introduce drift and typos. Centralize them into constants or an enum, such as `const PAR_STATUS = { GREEN: 'Green', WARNING: 'Warning', CRITICAL: 'Critical' } as const`, in a shared module.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

const accountabilityCount = visiblePersonnelStatuses.length + visibleTimers.length;

const refresh = useCallback(async () => {
await Promise.all([fetchTimerStatuses(callId), fetchPersonnelStatuses(callId), fetchResolvedTimers(callId)]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Batch failure vulnerability where Promise.all discards all fetched data if a single request fails. Use Promise.allSettled to inspect each result's status independently, allowing the UI to populate with successful data while targeting specific failures.

Kody rule violation: Use Promise.allSettled for batch operations with partial failures

Prompt for LLM

File src/components/command/accountability-section.tsx:

Line 62:

Batch failure vulnerability where `Promise.all` discards all fetched data if a single request fails. Use `Promise.allSettled` to inspect each result's status independently, allowing the UI to populate with successful data while targeting specific failures.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Comment thread src/stores/toast/store.ts Outdated
Comment on lines +34 to +38
setTimeout(() => {
set((state) => ({
toasts: state.toasts.filter((toast) => toast.id !== id),
}));
}, duration);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Orphaned callback leak caused by discarding the setTimeout return value, triggering unnecessary state updates via set() if the toast is manually dismissed. Capture the timer ID and call clearTimeout(timerId) inside removeToast to ensure a deterministic cleanup path.

Kody rule violation: Clear timers on teardown/unmount

Prompt for LLM

File src/stores/toast/store.ts:

Line 34 to 38:

Orphaned callback leak caused by discarding the `setTimeout` return value, triggering unnecessary state updates via `set()` if the toast is manually dismissed. Capture the timer ID and call `clearTimeout(timerId)` inside `removeToast` to ensure a deterministic cleanup path.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

@Resgrid-Bot

Resgrid-Bot commented Jul 23, 2026

Copy link
Copy Markdown

Code Review Completed! 🔥

The code review was successfully completed based on your current configurations.

Kody Guide: Usage and Configuration
Interacting with Kody
  • Request a Review: Ask Kody to review your PR manually by adding a comment with the @kody start-review command at the root of your PR.

  • Validate Business Logic: Ask Kody to validate your code against business rules by adding a comment with the @kody -v business-logic command.

  • Provide Feedback: Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

Current Kody Configuration
Review Options

The following review options are enabled or disabled:

Options Enabled
Bug
Performance
Security
Business Logic

Access your configuration settings here.

@ucswift
ucswift merged commit 862b006 into master Jul 23, 2026
8 checks passed
<View>
<Button
title="Load Feature"
onPress={() => setShowFeature(true)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Render performance degradation caused by inline arrow functions in JSX props, such as onPress={() => setShowFeature(true)}. These allocations create new function instances on every render; move function definitions outside the render method or use standard callbacks.

Kody rule violation: Avoid using .bind() or arrow functions in JSX props

Prompt for LLM

File .agents/skills/react-native-best-practices/references/bundle-code-splitting.md:

Line 150:

Render performance degradation caused by inline arrow functions in JSX props, such as `onPress={() => setShowFeature(true)}`. These allocations create new function instances on every render; move function definitions outside the render method or use standard callbacks.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

});

ScriptManager.shared.on('error', (scriptId, error) => {
console.error(`Failed: ${scriptId}`, error);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Unstructured logging: console.error uses template-string interpolation instead of structured fields, violating Rule [3] for observability searchability. Replace this with a structured logger call, such as logger.error('chunk_load_failed', { scriptId, err: error }).

Kody rule violation: Include error context in structured logs

Prompt for LLM

File .agents/skills/react-native-best-practices/references/bundle-code-splitting.md:

Line 231:

Unstructured logging: `console.error` uses template-string interpolation instead of structured fields, violating Rule [3] for observability searchability. Replace this with a structured logger call, such as `logger.error('chunk_load_failed', { scriptId, err: error })`.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

<TextInput
ref={inputRef}
defaultValue=""
onChangeText={(text) => console.log('Current:', text)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

PII exposure risk: Raw TextInput values are logged to the console without redaction, violating Rule [48] against default raw PII logging. Hash or tokenize sensitive values before logging, or log only non-identifying metadata such as input length.

Kody rule violation: Redact PII in logs and metrics by default

Prompt for LLM

File .agents/skills/react-native-best-practices/references/js-uncontrolled-components.md:

Line 109:

PII exposure risk: Raw `TextInput` values are logged to the console without redaction, violating Rule [48] against default raw PII logging. Hash or tokenize sensitive values before logging, or log only non-identifying metadata such as input length.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.


const handleChange = (text) => {
setQuery(text);
fetchResults(text).then(setResults);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Network flooding: onChangeText triggers fetchResults(text) on every keystroke without a debounce, violating Rule [53] and degrading UX with excessive requests. Wrap the fetch call in a debounce utility, such as lodash.debounce with a 250–400ms delay.

Kody rule violation: Debounce or throttle user input that triggers work

Prompt for LLM

File .agents/skills/react-native-best-practices/references/js-uncontrolled-components.md:

Line 128:

Network flooding: `onChangeText` triggers `fetchResults(text)` on every keystroke without a debounce, violating Rule [53] and degrading UX with excessive requests. Wrap the fetch call in a debounce utility, such as `lodash.debounce` with a 250–400ms delay.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Comment on lines +221 to +226
NotificationCenter.default.addObserver(
self,
selector: #selector(onJSLoad),
name: NSNotification.Name("RCTJavaScriptDidLoadNotification"),
object: nil
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Memory leak risk: The NotificationCenter observer lacks a deterministic removal path, which can cause leaks or fire on deallocated objects. Store the observer token and remove it within deinit using NotificationCenter.default.removeObserver(observer).

Kody rule violation: Provide error handlers to subscription/listener APIs

Prompt for LLM

File .agents/skills/react-native-best-practices/references/native-measure-tti.md:

Line 221 to 226:

Memory leak risk: The `NotificationCenter` observer lacks a deterministic removal path, which can cause leaks or fire on deallocated objects. Store the observer token and remove it within `deinit` using `NotificationCenter.default.removeObserver(observer)`.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

```tsx
import performance from 'react-native-performance';

export default function HomeScreen() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Maintainability constraint: The HomeScreen component uses a default export, which reduces refactoring clarity and auto-import capabilities. Replace it with a named export like export function HomeScreen().

Kody rule violation: Avoid default exports

Prompt for LLM

File .agents/skills/react-native-best-practices/references/native-measure-tti.md:

Line 173:

Maintainability constraint: The `HomeScreen` component uses a default export, which reduces refactoring clarity and auto-import capabilities. Replace it with a named export like `export function HomeScreen()`.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Comment on lines +232 to +238
ReactMarker.addListener { name ->
when (name) {
RUN_JS_BUNDLE_START -> { /* mark start */ }
RUN_JS_BUNDLE_END -> { /* mark end */ }
CONTENT_APPEARED -> { /* mark content */ }
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Resource leak: The ReactMarker listener is never removed during teardown, causing unnecessary resource consumption. Store the listener reference and remove it using the appropriate remove API during application or module teardown.

Kody rule violation: Proper memory management in event listeners

Prompt for LLM

File .agents/skills/react-native-best-practices/references/native-measure-tti.md:

Line 232 to 238:

Resource leak: The `ReactMarker` listener is never removed during teardown, causing unnecessary resource consumption. Store the listener reference and remove it using the appropriate remove API during application or module teardown.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.


const getEndpoint = (path: string): MockEndpoint => {
const callIndex = mockCreateApiEndpoint.mock.calls.findIndex(([endpoint]) => endpoint === path);
return mockCreateApiEndpoint.mock.results[callIndex].value as unknown as MockEndpoint;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

TypeError vulnerability: mock.results[callIndex] is indexed without verifying validity, meaning a -1 return from findIndex will throw a TypeError when accessing .value. Add a guard throwing an Error for invalid indices, or use optional chaining mock.results[callIndex]?.value.

Kody rule violation: Add null checks before accessing properties

Prompt for LLM

File src/api/check-in-timers/__tests__/check-in-timers.test.ts:

Line 33:

TypeError vulnerability: `mock.results[callIndex]` is indexed without verifying validity, meaning a `-1` return from `findIndex` will throw a `TypeError` when accessing `.value`. Add a guard throwing an `Error` for invalid indices, or use optional chaining `mock.results[callIndex]?.value`.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.


const getEndpoint = (path: string): MockEndpoint => {
const callIndex = mockCreateApiEndpoint.mock.calls.findIndex(([endpoint]) => endpoint === path);
return mockCreateApiEndpoint.mock.results[callIndex].value as unknown as MockEndpoint;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

TypeError vulnerability: Indexing mock.results with an unchecked callIndex from findIndex crashes when accessing .value on mock.results[-1]. Validate that callIndex >= 0 before indexing, or use mock.results.at(callIndex) with a null check.

Kody rule violation: Check query results before accessing indices

Prompt for LLM

File src/api/check-in-timers/__tests__/check-in-timers.test.ts:

Line 33:

TypeError vulnerability: Indexing `mock.results` with an unchecked `callIndex` from `findIndex` crashes when accessing `.value` on `mock.results[-1]`. Validate that `callIndex >= 0` before indexing, or use `mock.results.at(callIndex)` with a null check.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Enabled: enabled,
});
const endpoint = createApiEndpoint(`/CheckInTimers/ToggleCallTimers?callId=${encodeURIComponent(callId)}&enabled=${encodeURIComponent(enabled)}`);
const response = await endpoint.put<PerformCheckInResult>({});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Unhandled rejection risk: The awaited HTTP call lacks a try/catch guard, allowing PUT request failures to propagate without context. Wrap the await in a try/catch, log with structured context (callId, enabled, endpoint), and throw a typed error consistent with the CallPersonnelCheckInStatusesFetchError pattern in getCallPersonnelCheckInStatuses (lines 75-92).

Kody rule violation: Handle async operations with proper error handling

Prompt for LLM

File src/api/check-in-timers/check-in-timers.ts:

Line 97:

Unhandled rejection risk: The awaited HTTP call lacks a `try/catch` guard, allowing `PUT` request failures to propagate without context. Wrap the `await` in a `try/catch`, log with structured context (`callId`, `enabled`, `endpoint`), and throw a typed error consistent with the `CallPersonnelCheckInStatusesFetchError` pattern in `getCallPersonnelCheckInStatuses` (lines 75-92).

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Enabled: enabled,
});
const endpoint = createApiEndpoint(`/CheckInTimers/ToggleCallTimers?callId=${encodeURIComponent(callId)}&enabled=${encodeURIComponent(enabled)}`);
const response = await endpoint.put<PerformCheckInResult>({});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Unhandled rejection risk: The endpoint.put call lacks a try/catch wrapper, violating Rule [28] for network error mapping. Wrap the await call in a try/catch to log structured context (callId, enabled, endpoint) and throw an application-level error, matching the getCallPersonnelCheckInStatuses pattern.

Kody rule violation: Add try-catch blocks for external calls

Prompt for LLM

File src/api/check-in-timers/check-in-timers.ts:

Line 97:

Unhandled rejection risk: The `endpoint.put` call lacks a `try/catch` wrapper, violating Rule [28] for network error mapping. Wrap the `await` call in a `try/catch` to log structured context (`callId`, `enabled`, `endpoint`) and throw an application-level error, matching the `getCallPersonnelCheckInStatuses` pattern.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

CallId: callId,
Enabled: enabled,
});
const endpoint = createApiEndpoint(`/CheckInTimers/ToggleCallTimers?callId=${encodeURIComponent(callId)}&enabled=${encodeURIComponent(enabled)}`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Architectural inconsistency: The route /CheckInTimers/ToggleCallTimers is embedded inline in a template literal, breaking the centralized module-level constant pattern established by CALL_PERSONNEL_CHECK_IN_STATUSES_ENDPOINT. Extract the base path to a module-level constant like const TOGGLE_CALL_TIMERS_ENDPOINT = '/CheckInTimers/ToggleCallTimers'; and append query parameters at call time.

Kody rule violation: Centralize string constants

Prompt for LLM

File src/api/check-in-timers/check-in-timers.ts:

Line 96:

Architectural inconsistency: The route `/CheckInTimers/ToggleCallTimers` is embedded inline in a template literal, breaking the centralized module-level constant pattern established by `CALL_PERSONNEL_CHECK_IN_STATUSES_ENDPOINT`. Extract the base path to a module-level constant like `const TOGGLE_CALL_TIMERS_ENDPOINT = '/CheckInTimers/ToggleCallTimers';` and append query parameters at call time.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Comment thread src/app/(app)/_layout.tsx
<Pressable
className="p-3"
className="p-2"
hitSlop={4}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Magic number duplication: The numeric literal 4 for hitSlop is repeated across multiple components without a named constant. Extract this to a shared module-level constant like HIT_SLOP = 4 to ensure touch-target padding synchronization across CreateDrawerMenuButton and CreateHeaderBackButton.

Kody rule violation: Replace magic numbers with named constants

Prompt for LLM

File src/app/(app)/_layout.tsx:

Line 546:

Magic number duplication: The numeric literal `4` for `hitSlop` is repeated across multiple components without a named constant. Extract this to a shared module-level constant like `HIT_SLOP = 4` to ensure touch-target padding synchronization across `CreateDrawerMenuButton` and `CreateHeaderBackButton`.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Comment on lines +87 to +99
useEffect(() => {
setTimersEnabled(initialTimersEnabled);
setLoadedCallId(null);

if (!initialTimersEnabled) {
stopPolling();
return;
}

refresh();
startPolling(callId);
return stopPolling;
}, [callId, initialTimersEnabled, refresh, startPolling, stopPolling]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Bug high

Stale state regression: The AccountabilitySection's useEffect (lines 87-99) fails to re-run after src/app/call/[id].tsx (lines 239-241) invokes reset() on the shared useCheckInTimerStore during cleanup, leaving accountability data empty and polling stopped. Since the effect's dependencies [callId, initialTimersEnabled, refresh, startPolling, stopPolling] are stable references, subscribe to store data staleness or add a separate useEffect to trigger refresh when timerStatuses is unexpectedly empty.

Prompt for LLM

File src/components/command/accountability-section.tsx:

Line 87 to 99:

Stale state regression: The `AccountabilitySection`'s `useEffect` (lines 87-99) fails to re-run after `src/app/call/[id].tsx` (lines 239-241) invokes `reset()` on the shared `useCheckInTimerStore` during cleanup, leaving accountability data empty and polling stopped. Since the effect's dependencies `[callId, initialTimersEnabled, refresh, startPolling, stopPolling]` are stable references, subscribe to store data staleness or add a separate `useEffect` to trigger `refresh` when `timerStatuses` is unexpectedly empty.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

}

if (isOffline) {
activeToastIdRef.current = showToast(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Type safety violation: The string literal 'warning' represents a finite toast severity set without a strict type definition. Define a ToastType enum (e.g., enum ToastType { Warning='warning' }) and replace inline literals with enum references.

Kody rule violation: Use enums instead of magic strings

Prompt for LLM

File src/components/common/offline-status-toast.tsx:

Line 37:

Type safety violation: The string literal `'warning'` represents a finite toast severity set without a strict type definition. Define a `ToastType` enum (e.g., `enum ToastType { Warning='warning' }`) and replace inline literals with enum references.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

try {
const [lat, lng] = centerStr.split(',').map(Number);
if (isNaN(lat) || isNaN(lng)) return null;
if (!Number.isFinite(lat) || !Number.isFinite(lng) || lat < -90 || lat > 90 || lng < -180 || lng > 180) return null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Logic duplication: The coordinate validity check (isFinite + range) is duplicated in parseCenterLocation (line 101) and getPolygonBounds (line 128), risking implementation drift. Extract a shared isValidCoordinate(lat, lng): boolean helper to enforce single-source validation.

Kody rule violation: Extract duplicated logic into functions

Prompt for LLM

File src/lib/weather-alert-utils.ts:

Line 101:

Logic duplication: The coordinate validity check (`isFinite` + range) is duplicated in `parseCenterLocation` (line 101) and `getPolygonBounds` (line 128), risking implementation drift. Extract a shared `isValidCoordinate(lat, lng): boolean` helper to enforce single-source validation.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

};

export interface WeatherAlertMapBounds {
ne: [number, number];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Type ambiguity: The northeast bound uses an unnamed tuple [number, number], obscuring longitude and latitude element order. Replace it with a named interface like { longitude: number; latitude: number } or explicitly document the GeoJSON [lng, lat] convention.

Kody rule violation: Prefer Named Classes Over Tuples

Prompt for LLM

File src/lib/weather-alert-utils.ts:

Line 109:

Type ambiguity: The northeast bound uses an unnamed tuple `[number, number]`, obscuring longitude and latitude element order. Replace it with a named interface like `{ longitude: number; latitude: number }` or explicitly document the GeoJSON `[lng, lat]` convention.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

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