feat: Trail Condition Reporting with GPS, photos, offline support, and trust scoring#1907
feat: Trail Condition Reporting with GPS, photos, offline support, and trust scoring#1907
Conversation
…upport, and trust scoring Co-authored-by: andrew-bierman <94939237+andrew-bierman@users.noreply.github.com>
|
@coderabbitai Please review this PR for code quality, best practices, and potential issues. |
|
✅ Actions performedFull review triggered. |
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughAdds a trail condition reporting feature: UI screen and modal in the Expo app, React Query hooks for fetch/create/verify, new API routes with OpenAPI validation, a new database table and migration, and supporting types/schemas and utilities. Includes loading/error states and mocked fallback data. Changes
Sequence DiagramsequenceDiagram
actor User
participant Client as Expo App
participant Cache as React Query Cache
participant API as API Server
participant DB as Database
User->>Client: Open Trail Conditions screen
Client->>API: GET /api/trail-conditions
API->>DB: SELECT trail_conditions ORDER BY created_at DESC
DB-->>API: returns records
API-->>Client: returns TrailCondition[]
Client->>Cache: populate trailConditions query
Client-->>User: render cards (badges, trust, photos, coords)
User->>Client: Submit Report via ReportModal
Client->>API: POST /api/trail-conditions (Create request)
API->>DB: INSERT new trail_conditions row (compute initial trustScore)
DB-->>API: return created record
API-->>Client: return created TrailCondition
Client->>Cache: invalidate/refetch trailConditions
Client-->>User: show success and refresh list
User->>Client: Verify a report
Client->>API: POST /api/trail-conditions/{id}/verify
API->>DB: UPDATE verified_count++, adjust trust_score (cap 0.99)
DB-->>API: return updated record
API-->>Client: return updated TrailCondition
Client->>Cache: invalidate/refetch trailConditions
Client-->>User: display updated trust metrics
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (7)
packages/api/src/routes/trailConditions/index.ts (1)
5-5: Consider adding type bindings for consistency.The aggregator router lacks the
BindingsandVariablestype parameters that the sub-routes have. While this may work since the sub-routes are properly typed, adding explicit types would improve consistency and type safety.♻️ Proposed fix
+import type { Env } from '@packrat/api/types/env'; +import type { Variables } from '@packrat/api/types/variables'; + -const trailConditionsRoutes = new OpenAPIHono(); +const trailConditionsRoutes = new OpenAPIHono<{ Bindings: Env; Variables: Variables }>();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/api/src/routes/trailConditions/index.ts` at line 5, The aggregator router trailConditionsRoutes is created without explicit type parameters which breaks consistency with its typed sub-routes; update the instantiation of OpenAPIHono to include the same Bindings and Variables generic types used by the sub-routes (e.g., change new OpenAPIHono() to new OpenAPIHono<YourBindingsType, YourVariablesType>() or import/reuse the exact types exported by the sub-route modules) so trailConditionsRoutes has explicit type bindings matching the child routes.packages/api/src/db/schema.ts (1)
479-495: Consider adding indexes for query performance.The
list.tsroute queries bycreatedAt(ordering) and thereport.tsroute queries byuserId(for delete authorization and trust score calculation). Adding indexes would improve performance as the table grows.♻️ Proposed index additions
-}); +}, (table) => ({ + userIdIdx: index('trail_conditions_user_id_idx').on(table.userId), + createdAtIdx: index('trail_conditions_created_at_idx').on(table.createdAt), +}));🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/api/src/db/schema.ts` around lines 479 - 495, Add indexes to the trail_conditions table to speed queries: create an index on user_id to accelerate lookups/authorization and trust score calculations (referencing userId in trailConditions) and an index on created_at (or a descending index if your DB supports it) to optimize ordering used by list.ts (referencing createdAt in trailConditions); update the schema/migration to add these indexes and ensure they are created as part of deployment.packages/api/src/routes/trailConditions/list.ts (1)
38-48: Consider adding pagination for scalability.The list endpoint returns all trail condition reports without pagination. As the dataset grows, this will impact performance and bandwidth.
♻️ Suggested pagination approach
const listTrailConditionsRoute = createRoute({ method: 'get', path: '/', tags: ['Trail Conditions'], summary: 'List trail conditions', description: 'Get all trail condition reports ordered by most recent', security: [{ bearerAuth: [] }], + request: { + query: z.object({ + limit: z.coerce.number().min(1).max(100).default(20), + offset: z.coerce.number().min(0).default(0), + }), + }, responses: {Then in the handler:
const { limit, offset } = c.req.valid('query'); const items = await db .select() .from(trailConditions) .orderBy(desc(trailConditions.createdAt)) .limit(limit) .offset(offset);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/api/src/routes/trailConditions/list.ts` around lines 38 - 48, The list endpoint currently returns all records and should support pagination: update the handler registered via trailConditionListRoutes.openapi(listTrailConditionsRoute, ...) to read validated query params (e.g., via c.req.valid('query') to obtain limit and offset), then call createDb() and use the trailConditions table ordered by desc(trailConditions.createdAt) but add .limit(limit).offset(offset) to the query; also return total (either from a separate count query or include items.length as before) and validate/sanitize limit/offset defaults to prevent unbounded responses.packages/api/src/routes/trailConditions/report.ts (1)
133-164: Consider preventing self-voting and duplicate votes.Both verify and helpful routes allow users to vote on their own reports and vote multiple times. For trust score integrity, consider:
- Blocking self-verification (users shouldn't verify their own reports)
- Tracking which users have verified/marked helpful to prevent duplicate votes
This could be implemented with a separate
trail_condition_votesjunction table or by storing voter IDs in a JSONB array.Also applies to: 194-220
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/api/src/routes/trailConditions/report.ts` around lines 133 - 164, Prevent self-voting and duplicate votes by checking the current user and persisting votes before updating counts: in the verifyTrailConditionRoute handler (the function passed into trailConditionRoutes.openapi) get the current user ID from the request context (e.g., c.auth or c.req.user), return 403 if it matches report.userId to block self-verification, then check/record votes in a durable store (either add a trail_condition_votes junction table with columns like trail_condition_id, voter_id, created_at or add a voter_ids JSONB column on trailConditions) and, inside a DB transaction (use db.transaction), first query that vote record (or check JSONB contains) to return 409 if it already exists, otherwise insert the vote (or append the voter_id) and then update trailConditions via db.update(trailConditions).set(...).where(eq(trailConditions.id, reportId)) to increment verifiedCount and recalc trustScore, returning the updated report; ensure all DB operations use the same transaction to avoid race conditions.apps/expo/features/trail-conditions/types.ts (1)
24-29: Clarify intent:TrailConditionInputre-addsidafter omitting it.The type omits
idfromTrailConditionbut then re-adds it. This means the client is responsible for generating the ID before submission. This pattern works for offline-first scenarios but should be documented.Consider adding a brief comment explaining why the client generates the ID, or if server-generated IDs are preferred, remove
idfrom the input type and let the API assign it.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/expo/features/trail-conditions/types.ts` around lines 24 - 29, TrailConditionInput currently omits TrailCondition's id then re-adds it, making the client responsible for generating IDs; update the code by either (A) adding a brief inline comment above TrailConditionInput explaining the offline-first/client-generated-ID intent and why id is required on submissions, or (B) if IDs should be server-generated, remove the re-added id from TrailConditionInput so the API assigns it—refer to the TrailConditionInput and TrailCondition type declarations when making the change.apps/expo/app/(app)/trail-conditions.tsx (2)
23-85: Consider extracting mock data to a separate file.The mock data array is 60+ lines and clutters the screen component. For better separation of concerns, consider moving
TRAIL_CONDITIONS_MOCKto a dedicated file like__mocks__/trailConditions.tsorconstants/mockData.ts.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/expo/app/`(app)/trail-conditions.tsx around lines 23 - 85, The TRAIL_CONDITIONS_MOCK array is cluttering the component; extract it into a new module that exports the mock (e.g., export const TRAIL_CONDITIONS_MOCK = [...]) and update the component to import that symbol instead of declaring it inline; ensure the new file also exports any needed types (or import types like TrailConditionValue where used) and update the import path in trail-conditions.tsx so the component uses the extracted TRAIL_CONDITIONS_MOCK.
412-413: Fallback to mock data hides "no reports" state.When
datais an empty array (API returns no reports), the screen falls back to mock data. This may be confusing since users would see stale mock entries instead of an empty state indicating no reports exist yet.Consider showing an empty state when
datais an empty array, and only use mock data whendatais undefined (error/offline scenario):💡 Suggested refinement
- // Use API data if available, fall back to mock data - const trails: TrailCondition[] = data && data.length > 0 ? data : TRAIL_CONDITIONS_MOCK; + // Use API data if available; show mock data only on error/offline + const trails: TrailCondition[] = data ?? (isError ? TRAIL_CONDITIONS_MOCK : []);Then add an empty state message when
trails.length === 0 && !isLoading:{trails.length === 0 && !isLoading && ( <View className="items-center py-8"> <Text variant="body" className="text-muted-foreground"> No trail conditions reported yet. Be the first to report! </Text> </View> )}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/expo/app/`(app)/trail-conditions.tsx around lines 412 - 413, Currently the code falls back to TRAIL_CONDITIONS_MOCK whenever data is falsy or empty, which hides the "no reports" state; change the assignment so trails uses TRAIL_CONDITIONS_MOCK only when data is undefined (e.g., const trails = data === undefined ? TRAIL_CONDITIONS_MOCK : data), and preserve an empty array when the API returns no reports; then render an explicit empty state when trails.length === 0 && !isLoading with a user-facing message (use the existing isLoading flag and render logic in this component) so users see "No trail conditions reported yet" instead of mock entries.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/expo/app/`(app)/trail-conditions.tsx:
- Around line 112-120: The formatRelativeTime function can produce "NaN weeks
ago" for invalid date strings; update formatRelativeTime to validate the parsed
date (e.g., const d = new Date(dateStr); if (Number.isNaN(d.getTime())) return
'Unknown' or a safe fallback) before computing diff/days, and then continue with
the existing logic using that validated Date object.
- Around line 248-256: The current validation using parsedLat, parsedLng and
hasValidCoords only checks for numeric values; extend it to also verify latitude
is between -90 and 90 and longitude is between -180 and 180 before allowing
submission. Update the logic that sets hasValidCoords (or add a new boolean like
coordsInRange) to ensure parsedLat >= -90 && parsedLat <= 90 and parsedLng >=
-180 && parsedLng <= 180, and keep the existing NaN and trim checks; if the
range check fails, show the same Alert.alert('Invalid GPS', 'Please enter valid
numeric latitude and longitude values.') and return. Ensure you update any
early-return branch that currently uses hasValidCoords so the new range
constraints are enforced wherever parsedLat/parsedLng are used.
In `@packages/api/src/routes/trailConditions/list.ts`:
- Around line 90-97: Replace the full-row fetch used to compute existingReports
with a COUNT query: call db.select({ count: sql`count(*)`
}).from(trailConditions).where(eq(trailConditions.userId, auth.userId)) (or use
db.query to get a numeric count) and use that numeric count instead of
existingReports.length when computing baseScore; also add the required import
for sql from "drizzle-orm". Ensure you update references to
existingReports.length to the new count variable and keep the same baseScore
computation using Math.min(0.5 + count * 0.05, 0.9).
- Around line 82-87: Replace the raw JSON parse with the OpenAPI-validated body:
in the trailConditionListRoutes.openapi handler (the function passed for
createTrailConditionRoute) stop using c.req.json() and instead call
c.req.valid('json') to obtain the validated
CreateTrailConditionRequestSchema-typed body; then use that validated object for
the id check (and any other fields) so you rely on the route's schema validation
rather than parsing raw JSON.
In `@packages/api/src/routes/trailConditions/report.ts`:
- Around line 133-164: Current read-then-update in verifyTrailConditionRoute
causes a race condition; replace it with a single atomic update on the DB that
increments verifiedCount and caps trustScore in SQL. Remove the initial
db.query.trailConditions.findFirst call and instead call
db.update(trailConditions).set(...) with an expression to increment
verifiedCount (e.g. verifiedCount = verifiedCount + 1) and to compute trustScore
atomically using a SQL min/least expression (e.g. trustScore = LEAST(trustScore
+ 0.05, 0.99)), then .where(eq(trailConditions.id, reportId)).returning() and
handle the case where no row is returned (respond with error), keeping the
existing error handling in the catch block.
---
Nitpick comments:
In `@apps/expo/app/`(app)/trail-conditions.tsx:
- Around line 23-85: The TRAIL_CONDITIONS_MOCK array is cluttering the
component; extract it into a new module that exports the mock (e.g., export
const TRAIL_CONDITIONS_MOCK = [...]) and update the component to import that
symbol instead of declaring it inline; ensure the new file also exports any
needed types (or import types like TrailConditionValue where used) and update
the import path in trail-conditions.tsx so the component uses the extracted
TRAIL_CONDITIONS_MOCK.
- Around line 412-413: Currently the code falls back to TRAIL_CONDITIONS_MOCK
whenever data is falsy or empty, which hides the "no reports" state; change the
assignment so trails uses TRAIL_CONDITIONS_MOCK only when data is undefined
(e.g., const trails = data === undefined ? TRAIL_CONDITIONS_MOCK : data), and
preserve an empty array when the API returns no reports; then render an explicit
empty state when trails.length === 0 && !isLoading with a user-facing message
(use the existing isLoading flag and render logic in this component) so users
see "No trail conditions reported yet" instead of mock entries.
In `@apps/expo/features/trail-conditions/types.ts`:
- Around line 24-29: TrailConditionInput currently omits TrailCondition's id
then re-adds it, making the client responsible for generating IDs; update the
code by either (A) adding a brief inline comment above TrailConditionInput
explaining the offline-first/client-generated-ID intent and why id is required
on submissions, or (B) if IDs should be server-generated, remove the re-added id
from TrailConditionInput so the API assigns it—refer to the TrailConditionInput
and TrailCondition type declarations when making the change.
In `@packages/api/src/db/schema.ts`:
- Around line 479-495: Add indexes to the trail_conditions table to speed
queries: create an index on user_id to accelerate lookups/authorization and
trust score calculations (referencing userId in trailConditions) and an index on
created_at (or a descending index if your DB supports it) to optimize ordering
used by list.ts (referencing createdAt in trailConditions); update the
schema/migration to add these indexes and ensure they are created as part of
deployment.
In `@packages/api/src/routes/trailConditions/index.ts`:
- Line 5: The aggregator router trailConditionsRoutes is created without
explicit type parameters which breaks consistency with its typed sub-routes;
update the instantiation of OpenAPIHono to include the same Bindings and
Variables generic types used by the sub-routes (e.g., change new OpenAPIHono()
to new OpenAPIHono<YourBindingsType, YourVariablesType>() or import/reuse the
exact types exported by the sub-route modules) so trailConditionsRoutes has
explicit type bindings matching the child routes.
In `@packages/api/src/routes/trailConditions/list.ts`:
- Around line 38-48: The list endpoint currently returns all records and should
support pagination: update the handler registered via
trailConditionListRoutes.openapi(listTrailConditionsRoute, ...) to read
validated query params (e.g., via c.req.valid('query') to obtain limit and
offset), then call createDb() and use the trailConditions table ordered by
desc(trailConditions.createdAt) but add .limit(limit).offset(offset) to the
query; also return total (either from a separate count query or include
items.length as before) and validate/sanitize limit/offset defaults to prevent
unbounded responses.
In `@packages/api/src/routes/trailConditions/report.ts`:
- Around line 133-164: Prevent self-voting and duplicate votes by checking the
current user and persisting votes before updating counts: in the
verifyTrailConditionRoute handler (the function passed into
trailConditionRoutes.openapi) get the current user ID from the request context
(e.g., c.auth or c.req.user), return 403 if it matches report.userId to block
self-verification, then check/record votes in a durable store (either add a
trail_condition_votes junction table with columns like trail_condition_id,
voter_id, created_at or add a voter_ids JSONB column on trailConditions) and,
inside a DB transaction (use db.transaction), first query that vote record (or
check JSONB contains) to return 409 if it already exists, otherwise insert the
vote (or append the voter_id) and then update trailConditions via
db.update(trailConditions).set(...).where(eq(trailConditions.id, reportId)) to
increment verifiedCount and recalc trustScore, returning the updated report;
ensure all DB operations use the same transaction to avoid race conditions.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 71319b4b-c66d-4e44-b0c0-86e4c2ae7347
📒 Files selected for processing (15)
apps/expo/app/(app)/trail-conditions.tsxapps/expo/config.tsapps/expo/features/trail-conditions/hooks/index.tsapps/expo/features/trail-conditions/hooks/useCreateTrailConditionReport.tsapps/expo/features/trail-conditions/hooks/useTrailConditions.tsapps/expo/features/trail-conditions/hooks/useVerifyTrailConditionReport.tsapps/expo/features/trail-conditions/index.tsapps/expo/features/trail-conditions/types.tsapps/expo/features/trips/components/TrailConditionsTile.tsxpackages/api/src/db/schema.tspackages/api/src/routes/index.tspackages/api/src/routes/trailConditions/index.tspackages/api/src/routes/trailConditions/list.tspackages/api/src/routes/trailConditions/report.tspackages/api/src/schemas/trailConditions.ts
|
@copilot CodeRabbit has provided review feedback on this PR. Please address the comments and suggestions. Once you've made the necessary changes, I'll ask CodeRabbit to re-review. |
Co-authored-by: andrew-bierman <94939237+andrew-bierman@users.noreply.github.com>
Addressed all CodeRabbit feedback in dcef629:
|
|
@copilot CodeRabbit has reviewed this PR and provided feedback. Please address the review comments and push updates. Once you've made the changes, the monitor will ask CodeRabbit to re-review. |
All CodeRabbit feedback was already addressed in
The |
|
@copilot CodeRabbit has just reviewed this PR with feedback. Please review the comments and address any issues. Push updates when ready for re-review. |
All five CodeRabbit issues are already fixed in
The two |
|
@copilot CodeRabbit has reviewed this PR. Please address the feedback. |
All CodeRabbit feedback is already addressed in
The |
|
@copilot CodeRabbit has reviewed this PR. Please address the feedback. |
|
@copilot Please address the CodeRabbit review feedback on this PR (11 inline comments). |
All CodeRabbit feedback is already addressed in
The |
Co-authored-by: andrew-bierman <94939237+andrew-bierman@users.noreply.github.com>
Addressed in
The remaining non-outdated threads (GPS range validation at trail-conditions.tsx:251, migration at schema.ts:502, self-voting at report.ts:168) are all resolved in the current code — those fixes were landed in |
|
@copilot CodeRabbit has reviewed this PR. Please address the feedback. |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
|
@copilot CodeRabbit has reviewed this PR. Please address the feedback. |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
apps/expo/app/(app)/trail-conditions.tsx (1)
219-274: Consider disabling Submit button when form is invalid.The form currently validates on submit and shows alerts. For better UX, consider disabling the Submit button when required fields are empty, providing immediate feedback to users.
♻️ Optional: Add disabled state to Submit button
+ const isFormValid = trailName.trim().length > 0 && details.trim().length > 0; + return ( <Modal visible={visible} animationType="slide" presentationStyle="pageSheet"> ... - <TouchableOpacity onPress={handleSubmit} disabled={createReport.isPending}> + <TouchableOpacity onPress={handleSubmit} disabled={createReport.isPending || !isFormValid}> {createReport.isPending ? ( <ActivityIndicator size="small" color={colors.primary} /> ) : ( - <Text variant="body" className="font-semibold text-primary"> + <Text variant="body" className={cn('font-semibold', isFormValid ? 'text-primary' : 'text-muted-foreground')}> Submit </Text> )} </TouchableOpacity>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/expo/app/`(app)/trail-conditions.tsx around lines 219 - 274, ReportModal currently only validates on submit; compute a boolean (e.g., isFormValid) inside ReportModal based on existing state and validation logic (trailName.trim() non-empty, details.trim() non-empty, and coordinates either both empty or both present and in-range using parsedLat/parsedLng / hasValidCoords) and pass that to the Submit button's disabled prop (and adjust its style/opacity when disabled) so the Submit button is disabled until the form is valid; update the UI code that renders the Submit button to use this new boolean and ensure createReport.mutateAsync is only called when isFormValid is true.packages/api/src/routes/trailConditions/report.ts (1)
86-101: Consider atomic delete-first pattern for consistency.The delete route uses a read-then-delete pattern which has a minor TOCTOU window. While less critical than verify/markHelpful (since delete is idempotent), you could unify the approach by attempting the delete directly with both conditions and checking affected rows.
♻️ Optional: Atomic delete pattern
trailConditionRoutes.openapi(deleteTrailConditionRoute, async (c) => { const auth = c.get('user'); const db = createDb(c); const reportId = c.req.param('reportId'); try { - const report = await db.query.trailConditions.findFirst({ - where: and(eq(trailConditions.id, reportId), eq(trailConditions.userId, auth.userId)), - }); - - if (!report) return c.json({ error: 'Report not found or unauthorized' }, 403); - - await db + const deleted = await db .delete(trailConditions) - .where(and(eq(trailConditions.id, reportId), eq(trailConditions.userId, auth.userId))); + .where(and(eq(trailConditions.id, reportId), eq(trailConditions.userId, auth.userId))) + .returning({ id: trailConditions.id }); + if (deleted.length === 0) { + // Disambiguate: does the report exist but belong to another user? + const exists = await db.query.trailConditions.findFirst({ + where: eq(trailConditions.id, reportId), + columns: { id: true }, + }); + if (!exists) return c.json({ error: 'Report not found' }, 404); + return c.json({ error: 'Not authorized to delete this report' }, 403); + } return c.json({ success: true }, 200); } catch (error) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/api/src/routes/trailConditions/report.ts` around lines 86 - 101, Replace the read-then-delete (db.query.trailConditions.findFirst + db.delete(...).where(...)) with a single atomic delete call: call db.delete(trailConditions).where(and(eq(trailConditions.id, reportId), eq(trailConditions.userId, auth.userId))) and inspect the delete result (affected rows / rowCount / returned rows depending on your DB client) to determine if anything was removed; if zero rows were affected return c.json({ error: 'Report not found or unauthorized' }, 403) otherwise return c.json({ success: true }, 200). Keep the existing try/catch around the operation and preserve the error handling in the catch block.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/expo/app/`(app)/trail-conditions.tsx:
- Around line 403-408: The current fallback unconditionally uses
TRAIL_CONDITIONS_MOCK whenever data is falsy or empty, which hides whether the
API returned an empty array; update the logic around useTrailConditions() to
detect three states (loading/error, API returned empty array, and using mock
data) by setting a boolean like isUsingMockTrailData (set when data is
null/undefined and you substitute TRAIL_CONDITIONS_MOCK) and treating
data?.length === 0 as a genuine empty-API response; then use that flag in the
render (e.g., change the header text or show a badge/message) so the UI
distinguishes "No trail reports yet" (when !isLoading && !isError &&
data?.length === 0) from cases where mock data is being shown
(isUsingMockTrailData).
In `@packages/api/drizzle/meta/_journal.json`:
- Around line 243-249: The migration entry with idx 33 and tag
"0033_trail_conditions" has an out-of-order "when" timestamp (1741524000000);
update its "when" value to a timestamp later than the previous migration's
1760175950793 (e.g., current epoch ms or any value >1760175950793) so migration
chronology is preserved and tooling relying on chronological ordering won't be
confused; edit the "when" field for the record with idx 33 / tag
"0033_trail_conditions" accordingly.
---
Nitpick comments:
In `@apps/expo/app/`(app)/trail-conditions.tsx:
- Around line 219-274: ReportModal currently only validates on submit; compute a
boolean (e.g., isFormValid) inside ReportModal based on existing state and
validation logic (trailName.trim() non-empty, details.trim() non-empty, and
coordinates either both empty or both present and in-range using
parsedLat/parsedLng / hasValidCoords) and pass that to the Submit button's
disabled prop (and adjust its style/opacity when disabled) so the Submit button
is disabled until the form is valid; update the UI code that renders the Submit
button to use this new boolean and ensure createReport.mutateAsync is only
called when isFormValid is true.
In `@packages/api/src/routes/trailConditions/report.ts`:
- Around line 86-101: Replace the read-then-delete
(db.query.trailConditions.findFirst + db.delete(...).where(...)) with a single
atomic delete call: call
db.delete(trailConditions).where(and(eq(trailConditions.id, reportId),
eq(trailConditions.userId, auth.userId))) and inspect the delete result
(affected rows / rowCount / returned rows depending on your DB client) to
determine if anything was removed; if zero rows were affected return c.json({
error: 'Report not found or unauthorized' }, 403) otherwise return c.json({
success: true }, 200). Keep the existing try/catch around the operation and
preserve the error handling in the catch block.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 49182c47-a967-49eb-b449-061b8e411a89
📒 Files selected for processing (9)
apps/expo/app/(app)/trail-conditions.tsxapps/expo/lib/utils/getRelativeTime.tspackages/api/drizzle/0033_trail_conditions.sqlpackages/api/drizzle/meta/_journal.jsonpackages/api/src/db/schema.tspackages/api/src/routes/trailConditions/index.tspackages/api/src/routes/trailConditions/list.tspackages/api/src/routes/trailConditions/report.tspackages/api/src/schemas/trailConditions.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/api/src/db/schema.ts
|
@copilot CodeRabbit has reviewed this PR. Please address the feedback. |
|
@copilot CodeRabbit re-review still has 7 inline comments remaining. Please continue addressing the review feedback. |
|
👋 @copilot |
|
@copilot CodeRabbit has reviewed this PR. Please address the feedback. |
|
@copilot There are still 7 unresolved CodeRabbit inline comments. Please address the remaining review feedback. |
…ate distinction Co-authored-by: andrew-bierman <94939237+andrew-bierman@users.noreply.github.com>
|
@copilot CodeRabbit has reviewed this PR. Please address the feedback. |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
|
@copilot CodeRabbit has reviewed this PR. Please address the feedback. |
0033_trail_conditions.sql)limit/offsetand parallelCOUNT(*)for totalc.req.valid('json')for schema-validated request bodiesformatRelativeTime— use sharedgetRelativeTimeutility/verifyand/helpful_journal.json(Thread 13)Original prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.
Summary by CodeRabbit