Skip to content

Add onboarding flow and per-event crew roles#17

Open
heybeaux wants to merge 4 commits into
mainfrom
feature/onboarding-and-event-roles
Open

Add onboarding flow and per-event crew roles#17
heybeaux wants to merge 4 commits into
mainfrom
feature/onboarding-and-event-roles

Conversation

@heybeaux

@heybeaux heybeaux commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Onboarding (first launch only): new 4-slide paged walkthrough (src/screens/onboarding/OnboardingScreen.tsx) — plan your race, dial in your gear, rally your crew, and a premium CTA slide. "Start Premium" persists the completion flag before resetting navigation into the Subscription screen; Skip / "Maybe later" go straight into the app. Gate lives in AppNavigator behind the @ultraedge/onboarding-complete AsyncStorage flag, rendering a blank parchment view while the flag loads so onboarding never flashes for returning users.
  • Per-event crew roles: crew member profiles no longer carry a role/customRole — roles now live on EventCrewAssignment.roles: CrewRole[] (shared type + storage helpers in src/lib/eventCrew.ts). SelectCrewScreen gets multi-select role chips per member (with a custom label input when "Other" is chosen), so one person can be pacer + driver for one race and medic for another. CrewList/CrewDetail drop the global role badge; CrewDetail now lists assigned events with role chips; EventDetail and the race-plan PDF export render per-event roles.
  • Migration: one-time, idempotent local migration on CrewContext load (migrateLegacyCrewRoles) copies each member's legacy profile role onto all of their existing event assignments (single-element array, custom label preserved for "other"), then strips the legacy fields from stored member records. Covered by unit tests.

Test plan

  • npx tsc --noEmit passes
  • npx jest — 6 suites, 54 passed (includes new crewMigration.test.ts and onboarding.test.ts)
  • Simulator sanity check not run (skipped to keep the loop fast) — worth a quick manual pass on: first-launch onboarding → Skip / Start Premium paths, assigning crew with multiple roles, and upgrading from a build with legacy per-member roles

Follow-up note (Supabase)

The app is local-first and crew tables aren't wired to the API yet, so no schema changes were made here. When cloud sync for crew lands, supabase/migrations/007_crew.sql needs rework: event_crew.role is a single enum and must become an array or a junction table to match the new multi-role-per-assignment model (and crew_members.role/custom_role move off the profile).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added a first-launch onboarding flow with swipeable screens, skip/next controls, and completion tracking.
    • Crew members can now be assigned to events with one or more roles, including custom role names.
    • Event details now show assigned crew roles more clearly.
    • Added export support for the updated event crew and gear data.
  • Bug Fixes

    • Improved startup handling so users won’t see a brief onboarding flash.
    • Better fallback behavior when saved onboarding data can’t be read.

@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR restructures crew role assignment from a per-member profile field to per-event assignments, adding a shared eventCrew module with storage helpers and a legacy migration function, and updates CrewContext and related screens accordingly. It also adds a first-launch onboarding flow with persisted completion state, a new onboarding screen, and navigator routing, along with supporting Jest tests and mocks.

Changes

Per-event crew roles migration

Layer / File(s) Summary
Event crew storage and legacy migration
src/lib/eventCrew.ts, src/__tests__/crewMigration.test.ts
New module defines EventCrewAssignment types, loadEventCrewAssignments/saveEventCrewAssignments storage helpers, and migrateLegacyCrewRoles for idempotent migration of legacy member roles into assignments; tests cover propagation, stripping, "other" role handling, and idempotency.
CrewContext data model and load/create updates
src/context/CrewContext.tsx
CrewMember/Insert/Update interfaces drop role/customRole; loadCrewMembers reads both storage keys, runs migration, and persists changes; createCrewMember no longer sets role fields.
Crew create/edit forms drop role UI
src/screens/crew/CreateCrewScreen.tsx, src/screens/crew/EditCrewScreen.tsx
Removes role/custom-role state, payload fields, role selection UI, and associated styles from both screens.
Crew list and detail screens show per-event assignments
src/screens/crew/CrewListScreen.tsx, src/screens/crew/CrewDetailScreen.tsx
Removes role filtering/badges from the list; detail screen loads assignments on focus and renders per-event role chips.
Event screens: select crew with roles and display role chips
src/screens/events/SelectCrewScreen.tsx, src/screens/events/EventDetailScreen.tsx, src/components/ExportRacePlanButton.tsx
Select screen adds multi-role chip selection and custom-role input persisted per assignment; detail screen and export button render/derive role labels from assignments instead of member records.

First-launch onboarding flow

Layer / File(s) Summary
Onboarding persistence helper
src/lib/onboarding.ts, src/__tests__/onboarding.test.ts, jest.setup.js
Adds ONBOARDING_COMPLETE_KEY, isOnboardingComplete, setOnboardingComplete with tests and a Jest AsyncStorage mock.
OnboardingScreen component
src/screens/onboarding/OnboardingScreen.tsx
New paged four-slide onboarding UI with dot indicators, skip/next controls, and completion persistence.
AppNavigator onboarding-first routing
src/navigation/AppNavigator.tsx
Checks completion state on mount, shows a blank screen while loading, and sets initial route to Onboarding or Main.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CrewContext
  participant migrateLegacyCrewRoles
  participant AsyncStorage
  CrewContext->>AsyncStorage: load crew members and event assignments
  CrewContext->>migrateLegacyCrewRoles: pass members and assignments
  migrateLegacyCrewRoles->>migrateLegacyCrewRoles: copy legacy role into roles array
  migrateLegacyCrewRoles->>migrateLegacyCrewRoles: strip role/customRole from member
  migrateLegacyCrewRoles-->>CrewContext: return members, assignments, changed
  CrewContext->>AsyncStorage: persist updated data if changed
Loading
sequenceDiagram
  participant AppNavigator
  participant isOnboardingComplete
  participant OnboardingScreen
  AppNavigator->>isOnboardingComplete: check flag on mount
  isOnboardingComplete-->>AppNavigator: return boolean
  AppNavigator->>AppNavigator: set initialRouteName Onboarding or Main
  OnboardingScreen->>OnboardingScreen: user completes slides
  OnboardingScreen->>AppNavigator: reset navigation to Main or Subscription
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the two main changes: onboarding flow and per-event crew roles.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/onboarding-and-event-roles

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (4)
src/context/CrewContext.tsx (1)

106-143: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Migration wiring looks correct.

Concurrent read/migrate/write is race-free enough here (migration is a pure function of the snapshot, so a redundant overlapping call just rewrites the same values). One minor note: the double cast members as unknown as CrewMember[] at Line 132 bypasses structural typing — StoredCrewMemberRecord only guarantees id: string, so a legacy record missing name/phone/etc. would silently produce an ill-typed CrewMember. Not a regression from this PR, but worth tightening if legacy data shapes are a concern.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/context/CrewContext.tsx` around lines 106 - 143, The cast in
loadCrewMembers is too permissive and can hide invalid legacy records, so
tighten the typing at the CrewContext migration/load path. In loadCrewMembers,
validate or map the migrated StoredCrewMemberRecord data into a proper
CrewMember shape before calling setCrewMembers, rather than using members as
unknown as CrewMember[]. Keep the existing migrateLegacyCrewRoles flow, but
ensure only records with the required CrewMember fields are accepted or
normalized before sorting and storing.
src/lib/eventCrew.ts (1)

8-8: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Type-only circular import between eventCrew.ts and CrewContext.tsx.

eventCrew.ts imports CrewRole (type-only) from ../context/CrewContext, while CrewContext.tsx imports EVENT_CREW_KEY/migrateLegacyCrewRoles/etc. from ../lib/eventCrew. This is a circular module reference. import type is normally erased at compile time so this shouldn't break at runtime, but it's fragile — a future edit that turns CrewRole into a value import (e.g., adding a ROLE_CONFIG re-export) would introduce a real circular dependency.

Consider moving CrewRole (and other shared crew domain types) into a small dedicated types module (e.g. src/types/crew.ts) that both files import from.

Also applies to: 10-10

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/eventCrew.ts` at line 8, The import in eventCrew.ts creates a fragile
circular dependency with CrewContext.tsx because CrewRole is being pulled from
the context module even though both files share crew domain types. Move CrewRole
and any other shared crew types out of CrewContext.tsx into a dedicated shared
types module, then update eventCrew.ts and CrewContext.tsx to import from that
module instead of referencing each other directly. Keep the existing eventCrew
symbols like EVENT_CREW_KEY and migrateLegacyCrewRoles in place, but ensure
CrewContext.tsx no longer serves as the source for shared type definitions.
src/screens/events/EventDetailScreen.tsx (1)

507-511: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Inconsistent empty-roles fallback vs. CrewDetailScreen.

When roles.length === 0, this shows the member's phone number as secondary text, whereas CrewDetailScreen's equivalent section always shows "No roles set" regardless of phone. Consider using the same "No roles set" fallback here for consistency.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/screens/events/EventDetailScreen.tsx` around lines 507 - 511, The
empty-roles fallback in EventDetailScreen is inconsistent with CrewDetailScreen
because the ternary in the member details rendering uses member.phone when
roles.length is 0. Update the EventDetailScreen section that renders the member
secondary text so it always shows "No roles set" for the no-roles case, matching
CrewDetailScreen’s behavior and keeping the fallback consistent.
src/components/ExportRacePlanButton.tsx (1)

21-24: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider reusing loadEventCrewAssignments() instead of the generic readJson + EVENT_CREW_KEY.

This bypasses the shared eventCrew.ts load helper in favor of the generic readJson<T>() utility. Functionally equivalent today, but centralizing on loadEventCrewAssignments() avoids two parallel implementations of the same read logic drifting apart later.

Also applies to: 36-51, 88-91

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/ExportRacePlanButton.tsx` around lines 21 - 24, The import and
read path in ExportRacePlanButton should use the shared eventCrew loader instead
of duplicating JSON access with EVENT_CREW_KEY and readJson<T>(). Update the
component to rely on loadEventCrewAssignments() from eventCrew.ts, and adjust
any related mapping/loading logic in the affected sections so the export flow
reads assignments through that single helper. This keeps ExportRacePlanButton
aligned with the centralized loading behavior and removes the parallel
implementation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lib/eventCrew.ts`:
- Around line 12-20: The event-crew save flow can still persist duplicate rows
because `handleSave` appends `newAssignments` to `allCrew` and
`saveEventCrewAssignments` writes the array unchanged even when the same crew
member is re-selected. Update the `EventCrewAssignment` handling in
`eventCrew.ts` so `handleSave` or `saveEventCrewAssignments` deduplicates by the
`(eventId, crewMemberId)` pair before persisting, and ensure the selection logic
tied to `alreadyAddedIds` in `availableCrew` cannot re-add an existing
assignment once current rows are loaded.

In `@src/screens/crew/CrewDetailScreen.tsx`:
- Around line 245-309: The Assigned Events list in CrewDetailScreen still
renders orphaned EventCrewAssignment records as “Unknown event” after an event
is deleted. Update the event-deletion flow to also remove all related
assignments (or cascade-delete them at the storage layer) so CrewDetailScreen’s
getEvent/assignments.map path only sees valid events and no stale rows remain.

In `@src/screens/events/EventDetailScreen.tsx`:
- Around line 44-52: Event crew persistence is still being handled manually in
EventDetailScreen instead of using the shared helpers from eventCrew. Replace
the direct AsyncStorage getItem/setItem plus JSON.parse/stringify logic with
loadEventCrewAssignments() and saveEventCrewAssignments() from
../../lib/eventCrew, and keep the existing EVENT_CREW_KEY/EventCrewAssignment
references aligned with that module. Update the crew-loading and crew-saving
paths in EventDetailScreen so all event-crew reads and writes go through the
centralized helper functions and no duplicate persistence logic remains.

---

Nitpick comments:
In `@src/components/ExportRacePlanButton.tsx`:
- Around line 21-24: The import and read path in ExportRacePlanButton should use
the shared eventCrew loader instead of duplicating JSON access with
EVENT_CREW_KEY and readJson<T>(). Update the component to rely on
loadEventCrewAssignments() from eventCrew.ts, and adjust any related
mapping/loading logic in the affected sections so the export flow reads
assignments through that single helper. This keeps ExportRacePlanButton aligned
with the centralized loading behavior and removes the parallel implementation.

In `@src/context/CrewContext.tsx`:
- Around line 106-143: The cast in loadCrewMembers is too permissive and can
hide invalid legacy records, so tighten the typing at the CrewContext
migration/load path. In loadCrewMembers, validate or map the migrated
StoredCrewMemberRecord data into a proper CrewMember shape before calling
setCrewMembers, rather than using members as unknown as CrewMember[]. Keep the
existing migrateLegacyCrewRoles flow, but ensure only records with the required
CrewMember fields are accepted or normalized before sorting and storing.

In `@src/lib/eventCrew.ts`:
- Line 8: The import in eventCrew.ts creates a fragile circular dependency with
CrewContext.tsx because CrewRole is being pulled from the context module even
though both files share crew domain types. Move CrewRole and any other shared
crew types out of CrewContext.tsx into a dedicated shared types module, then
update eventCrew.ts and CrewContext.tsx to import from that module instead of
referencing each other directly. Keep the existing eventCrew symbols like
EVENT_CREW_KEY and migrateLegacyCrewRoles in place, but ensure CrewContext.tsx
no longer serves as the source for shared type definitions.

In `@src/screens/events/EventDetailScreen.tsx`:
- Around line 507-511: The empty-roles fallback in EventDetailScreen is
inconsistent with CrewDetailScreen because the ternary in the member details
rendering uses member.phone when roles.length is 0. Update the EventDetailScreen
section that renders the member secondary text so it always shows "No roles set"
for the no-roles case, matching CrewDetailScreen’s behavior and keeping the
fallback consistent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c917673c-631a-4e1d-84b6-0f3af6dd54e9

📥 Commits

Reviewing files that changed from the base of the PR and between dcf46b4 and 5921775.

📒 Files selected for processing (15)
  • jest.setup.js
  • src/__tests__/crewMigration.test.ts
  • src/__tests__/onboarding.test.ts
  • src/components/ExportRacePlanButton.tsx
  • src/context/CrewContext.tsx
  • src/lib/eventCrew.ts
  • src/lib/onboarding.ts
  • src/navigation/AppNavigator.tsx
  • src/screens/crew/CreateCrewScreen.tsx
  • src/screens/crew/CrewDetailScreen.tsx
  • src/screens/crew/CrewListScreen.tsx
  • src/screens/crew/EditCrewScreen.tsx
  • src/screens/events/EventDetailScreen.tsx
  • src/screens/events/SelectCrewScreen.tsx
  • src/screens/onboarding/OnboardingScreen.tsx

Comment thread src/lib/eventCrew.ts
Comment on lines +12 to +20
export interface EventCrewAssignment {
eventId: string;
crewMemberId: string;
/** A member can hold multiple roles for a single event. */
roles: CrewRole[];
/** Custom label used when `roles` includes 'other'. */
customRole?: string | null;
notes?: string;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect how SelectCrewScreen initializes selectedIds and whether it excludes existing assignments
rg -n -B3 -A40 'const handleSave' src/screens/events/SelectCrewScreen.tsx
rg -n -B5 -A15 'selectedIds' src/screens/events/SelectCrewScreen.tsx | head -80

Repository: beaux-riel/UltraEdge

Length of output: 4363


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== SelectCrewScreen.tsx relevant section ==\n'
sed -n '45,125p' src/screens/events/SelectCrewScreen.tsx

printf '\n== saveEventCrewAssignments definition(s) ==\n'
rg -n -A80 -B20 'function saveEventCrewAssignments|const saveEventCrewAssignments|saveEventCrewAssignments\(' src

Repository: beaux-riel/UltraEdge

Length of output: 15697


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== List/render section ==\n'
sed -n '125,310p' src/screens/events/SelectCrewScreen.tsx

printf '\n== crewMembers hook definition ==\n'
rg -n -A80 -B20 'function useCrewMembers|const useCrewMembers|export .*useCrewMembers' src

Repository: beaux-riel/UltraEdge

Length of output: 8095


Prevent duplicate event-crew rows

availableCrew filters out alreadyAddedIds, but handleSave still appends [...allCrew, ...newAssignments] and saveEventCrewAssignments writes the array as-is. Because alreadyAddedIds starts empty and there’s no save-time dedupe, re-selecting an already assigned crew member can still persist a duplicate row. Dedup by (eventId, crewMemberId) or block selection until existing assignments load.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/eventCrew.ts` around lines 12 - 20, The event-crew save flow can
still persist duplicate rows because `handleSave` appends `newAssignments` to
`allCrew` and `saveEventCrewAssignments` writes the array unchanged even when
the same crew member is re-selected. Update the `EventCrewAssignment` handling
in `eventCrew.ts` so `handleSave` or `saveEventCrewAssignments` deduplicates by
the `(eventId, crewMemberId)` pair before persisting, and ensure the selection
logic tied to `alreadyAddedIds` in `availableCrew` cannot re-add an existing
assignment once current rows are loaded.

Comment on lines +245 to +309
{/* Assigned Events with per-event roles */}
<View style={styles.eventsSection}>
<H3 style={{ marginBottom: spacing.md }}>Assigned Events</H3>
<Card variant="standard">
<CardContent>
<View style={styles.placeholderContent}>
<Ionicons name="calendar-outline" size={32} color={colors.mist} />
<Body color="secondary" style={{ marginTop: spacing.sm, textAlign: 'center' }}>
Event assignments coming soon!
</Body>
<BodySmall color="tertiary" style={{ marginTop: spacing.xs, textAlign: 'center' }}>
You'll be able to assign crew members to specific events and checkpoints.
</BodySmall>
</View>
{assignments.length === 0 ? (
<View style={styles.placeholderContent}>
<Ionicons name="calendar-outline" size={32} color={colors.mist} />
<Body color="secondary" style={{ marginTop: spacing.sm, textAlign: 'center' }}>
Not assigned to any events yet.
</Body>
<BodySmall color="tertiary" style={{ marginTop: spacing.xs, textAlign: 'center' }}>
Assign {member.name.split(' ')[0]} to an event to set their roles for race day.
</BodySmall>
</View>
) : (
assignments.map((assignment, index) => {
const event = getEvent(assignment.eventId);
const roles = assignment.roles ?? [];

return (
<View
key={`${assignment.eventId}-${assignment.crewMemberId}`}
style={[
styles.eventRow,
{
borderBottomColor: colors.borderLight,
borderBottomWidth: index < assignments.length - 1 ? 1 : 0,
},
]}
>
<Body numberOfLines={1} style={{ fontWeight: '600' }}>
{event?.name ?? 'Unknown event'}
</Body>
<View style={styles.roleChips}>
{roles.length === 0 ? (
<BodySmall color="tertiary">No roles set</BodySmall>
) : (
roles.map(role => {
const config = ROLE_CONFIG[role];
const label =
role === 'other' && assignment.customRole
? assignment.customRole
: config.label;
return (
<View
key={role}
style={[styles.roleChip, { backgroundColor: config.color + '20' }]}
>
<Ionicons name={config.icon as any} size={12} color={config.color} />
<Text
variant="bodySmall"
style={{ color: config.color, marginLeft: 4 }}
>
{label}
</Text>
</View>
);
})
)}
</View>
</View>
);
})
)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check whether deleteEvent (or any event-deletion path) cleans up EventCrewAssignment / EVENT_CREW_KEY entries
rg -n -A15 'deleteEvent' src/context/EventContext.tsx
rg -n 'EVENT_CREW_KEY|EventCrewAssignment' src/context/EventContext.tsx src/lib/eventCrew.ts

Repository: beaux-riel/UltraEdge

Length of output: 2587


Delete crew assignments when an event is removed. This leaves orphaned EventCrewAssignment rows behind, so members keep seeing Unknown event entries here and the backing storage never gets cleaned up.
[medium_effort_and_high_reward]

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/screens/crew/CrewDetailScreen.tsx` around lines 245 - 309, The Assigned
Events list in CrewDetailScreen still renders orphaned EventCrewAssignment
records as “Unknown event” after an event is deleted. Update the event-deletion
flow to also remove all related assignments (or cascade-delete them at the
storage layer) so CrewDetailScreen’s getEvent/assignments.map path only sees
valid events and no stale rows remain.

Comment on lines +44 to 52
import { EVENT_CREW_KEY, EventCrewAssignment } from '../../lib/eventCrew';
import { eventStatsFromRoute } from '../../lib/gpx';
import GPXRouteSection from '../../components/gpx/GPXRouteSection';
import ExportRacePlanButton from '../../components/ExportRacePlanButton';

type Props = NativeStackScreenProps<any, 'EventDetail'>;

// Storage keys for event relationships
// Storage key for event gear relationships
const EVENT_GEAR_KEY = '@ultraedge/event-gear';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Duplicated AsyncStorage access instead of the new shared eventCrew helpers.

This file imports EVENT_CREW_KEY/EventCrewAssignment from ../../lib/eventCrew but still manually calls AsyncStorage.getItem/setItem + JSON.parse/stringify for crew data (Lines 87-106, 189-201) instead of using loadEventCrewAssignments()/saveEventCrewAssignments() from the same module. This duplicates logic the migration introduced specifically to centralize event-crew persistence, and risks the two code paths silently diverging.

♻️ Proposed refactor using the shared helpers
-import { EVENT_CREW_KEY, EventCrewAssignment } from '../../lib/eventCrew';
+import {
+  EventCrewAssignment,
+  loadEventCrewAssignments,
+  saveEventCrewAssignments,
+} from '../../lib/eventCrew';
   const loadRelationships = useCallback(async () => {
     try {
-      const [gearData, crewData] = await Promise.all([
-        AsyncStorage.getItem(EVENT_GEAR_KEY),
-        AsyncStorage.getItem(EVENT_CREW_KEY),
-      ]);
-      
-      if (gearData) {
-        const allGear: EventGearAllocation[] = JSON.parse(gearData);
-        setEventGear(allGear.filter(g => g.eventId === eventId));
-      }
-      
-      if (crewData) {
-        const allCrew: EventCrewAssignment[] = JSON.parse(crewData);
-        setEventCrew(allCrew.filter(c => c.eventId === eventId));
-      }
+      const gearData = await AsyncStorage.getItem(EVENT_GEAR_KEY);
+      if (gearData) {
+        const allGear: EventGearAllocation[] = JSON.parse(gearData);
+        setEventGear(allGear.filter(g => g.eventId === eventId));
+      }
+      const allCrew = await loadEventCrewAssignments();
+      setEventCrew(allCrew.filter(c => c.eventId === eventId));
     } catch (error) {
       console.error('Failed to load event relationships:', error);
     }
   }, [eventId]);
         onPress: async () => {
           try {
-            const stored = await AsyncStorage.getItem(EVENT_CREW_KEY);
-            const allCrew: EventCrewAssignment[] = stored ? JSON.parse(stored) : [];
+            const allCrew = await loadEventCrewAssignments();
             const updated = allCrew.filter(c => !(c.eventId === eventId && c.crewMemberId === crewMemberId));
-            await AsyncStorage.setItem(EVENT_CREW_KEY, JSON.stringify(updated));
+            await saveEventCrewAssignments(updated);
             setEventCrew(updated.filter(c => c.eventId === eventId));

Also applies to: 87-106, 177-201

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/screens/events/EventDetailScreen.tsx` around lines 44 - 52, Event crew
persistence is still being handled manually in EventDetailScreen instead of
using the shared helpers from eventCrew. Replace the direct AsyncStorage
getItem/setItem plus JSON.parse/stringify logic with loadEventCrewAssignments()
and saveEventCrewAssignments() from ../../lib/eventCrew, and keep the existing
EVENT_CREW_KEY/EventCrewAssignment references aligned with that module. Update
the crew-loading and crew-saving paths in EventDetailScreen so all event-crew
reads and writes go through the centralized helper functions and no duplicate
persistence logic remains.

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