Skip to content

Commit 45c28ab

Browse files
committed
fix: biome line length and unused variable in duplicate pack
1 parent c571fdc commit 45c28ab

File tree

26 files changed

+4090
-2
lines changed

26 files changed

+4090
-2
lines changed
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
import { LargeTitleHeader, Text } from '@packrat/ui/nativewindui';
2+
import { Icon } from '@roninoss/icons';
3+
import {
4+
TRAIL_CONDITION_SEVERITIES,
5+
TRAIL_CONDITION_TYPES,
6+
type TrailConditionMarker,
7+
useCurrentLocation,
8+
useTrailConditions,
9+
} from 'expo-app/features/trail-conditions';
10+
import { cn } from 'expo-app/lib/cn';
11+
import { router } from 'expo-router';
12+
import { useCallback, useRef, useState } from 'react';
13+
import { ActivityIndicator, TouchableOpacity, View } from 'react-native';
14+
import MapView, { Callout, Marker, Region } from 'react-native-maps';
15+
16+
const severityColors: Record<string, string> = {
17+
low: '#22c55e',
18+
moderate: '#f59e0b',
19+
high: '#f97316',
20+
closed: '#dc2626',
21+
};
22+
23+
const conditionIcons: Record<string, string> = {
24+
mud: 'water',
25+
snow: 'snowflake',
26+
ice: 'alert-circle',
27+
fallen_trees: 'tree',
28+
water_crossing: 'waves',
29+
flooding: 'flood',
30+
bridge_out: 'bridge',
31+
washout: 'water-alert',
32+
overgrown: 'grass',
33+
fire_closure: 'fire',
34+
wildlife: 'bird',
35+
hazard: 'alert-octagon',
36+
rockfall: 'mountain',
37+
maintenance: 'wrench',
38+
other: 'alert',
39+
};
40+
41+
function formatDate(dateStr: string): string {
42+
const date = new Date(dateStr);
43+
const now = new Date();
44+
const diffMs = now.getTime() - date.getTime();
45+
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
46+
const diffDays = Math.floor(diffHours / 24);
47+
48+
if (diffHours < 1) return 'Just now';
49+
if (diffHours < 24) return `${diffHours}h ago`;
50+
if (diffDays < 7) return `${diffDays}d ago`;
51+
return date.toLocaleDateString();
52+
}
53+
54+
function MapMarker({ marker }: { marker: TrailConditionMarker }) {
55+
const color = severityColors[marker.severity] || '#6b7280';
56+
const conditionType = TRAIL_CONDITION_TYPES.find((t) => t.value === marker.conditionType);
57+
58+
return (
59+
<Marker
60+
coordinate={{
61+
latitude: marker.latitude,
62+
longitude: marker.longitude,
63+
}}
64+
pinColor={color}
65+
>
66+
<Callout tooltip>
67+
<View className="w-64 rounded-lg bg-card p-3 shadow-xl">
68+
<View className="flex-row items-center justify-between">
69+
<Text variant="subhead" className="flex-1 font-semibold">
70+
{marker.title}
71+
</Text>
72+
<View className={cn('rounded-full px-2 py-0.5')} style={{ backgroundColor: color }}>
73+
<Text variant="caption2" className="text-white">
74+
{marker.severity}
75+
</Text>
76+
</View>
77+
</View>
78+
79+
{marker.description && (
80+
<Text variant="footnote" className="mt-1 text-muted-foreground">
81+
{marker.description}
82+
</Text>
83+
)}
84+
85+
<View className="mt-2 flex-row items-center justify-between">
86+
<Text variant="caption1" className="text-muted-foreground">
87+
{formatDate(marker.createdAt)}
88+
</Text>
89+
{marker.trustScore !== undefined && (
90+
<View className="flex-row items-center gap-1">
91+
<Icon name="shield-check" size={12} color="#666" />
92+
<Text variant="caption1" className="text-muted-foreground">
93+
{marker.trustScore}%
94+
</Text>
95+
</View>
96+
)}
97+
</View>
98+
</View>
99+
</Callout>
100+
</Marker>
101+
);
102+
}
103+
104+
export default function TrailConditionsMapScreen() {
105+
const mapRef = useRef<MapView>(null);
106+
const { markers, isLoading, error, refresh } = useTrailConditions();
107+
const { location, isLoading: isLocationLoading } = useCurrentLocation();
108+
const [selectedFilter, setSelectedFilter] = useState<string | null>(null);
109+
110+
const filteredMarkers = selectedFilter
111+
? markers.filter((m) => m.conditionType === selectedFilter)
112+
: markers;
113+
114+
const onMapReady = useCallback(() => {
115+
if (location && mapRef.current) {
116+
mapRef.current.animateToRegion({
117+
latitude: location.coords.latitude,
118+
longitude: location.coords.longitude,
119+
latitudeDelta: 0.0922,
120+
longitudeDelta: 0.0421,
121+
});
122+
}
123+
}, [location]);
124+
125+
return (
126+
<>
127+
<LargeTitleHeader title="Trail Conditions Map" />
128+
<View className="flex-1">
129+
{/* Filter Bar */}
130+
<View className="border-b border-border bg-card p-2">
131+
<View className="flex-row gap-2">
132+
<TouchableOpacity
133+
onPress={() => setSelectedFilter(null)}
134+
className={cn(
135+
'rounded-full px-3 py-1',
136+
selectedFilter === null ? 'bg-primary' : 'bg-muted',
137+
)}
138+
>
139+
<Text variant="caption1" className={selectedFilter === null ? 'text-white' : ''}>
140+
All
141+
</Text>
142+
</TouchableOpacity>
143+
{TRAIL_CONDITION_TYPES.slice(0, 5).map((type) => (
144+
<TouchableOpacity
145+
key={type.value}
146+
onPress={() => setSelectedFilter(selectedFilter === type.value ? null : type.value)}
147+
className={cn(
148+
'rounded-full px-3 py-1',
149+
selectedFilter === type.value ? 'bg-primary' : 'bg-muted',
150+
)}
151+
>
152+
<Text
153+
variant="caption1"
154+
className={selectedFilter === type.value ? 'text-white' : ''}
155+
>
156+
{type.label}
157+
</Text>
158+
</TouchableOpacity>
159+
))}
160+
</View>
161+
</View>
162+
163+
{/* Map */}
164+
<View className="flex-1">
165+
{isLoading || isLocationLoading ? (
166+
<View className="flex-1 items-center justify-center">
167+
<ActivityIndicator size="large" />
168+
<Text className="mt-2 text-muted-foreground">Loading map...</Text>
169+
</View>
170+
) : error ? (
171+
<View className="flex-1 items-center justify-center p-4">
172+
<Text className="text-center text-red-500">{error}</Text>
173+
<TouchableOpacity onPress={refresh} className="mt-4 rounded-lg bg-primary px-4 py-2">
174+
<Text className="text-white">Retry</Text>
175+
</TouchableOpacity>
176+
</View>
177+
) : (
178+
<MapView
179+
ref={mapRef}
180+
className="flex-1"
181+
onMapReady={onMapReady}
182+
initialRegion={
183+
location
184+
? {
185+
latitude: location.coords.latitude,
186+
longitude: location.coords.longitude,
187+
latitudeDelta: 0.0922,
188+
longitudeDelta: 0.0421,
189+
}
190+
: {
191+
latitude: 39.8283,
192+
longitude: -98.5795,
193+
latitudeDelta: 40,
194+
longitudeDelta: 40,
195+
}
196+
}
197+
>
198+
{filteredMarkers.map((marker) => (
199+
<MapMarker key={marker.id} marker={marker} />
200+
))}
201+
</MapView>
202+
)}
203+
</View>
204+
205+
{/* Legend */}
206+
<View className="absolute bottom-4 left-4 rounded-lg bg-card/90 p-2 shadow-lg">
207+
<Text variant="caption2" className="mb-1 font-semibold">
208+
Severity
209+
</Text>
210+
{TRAIL_CONDITION_SEVERITIES.map((severity) => (
211+
<View key={severity.value} className="flex-row items-center gap-1">
212+
<View
213+
className="h-2 w-2 rounded-full"
214+
style={{ backgroundColor: severityColors[severity.value] }}
215+
/>
216+
<Text variant="caption2">{severity.label}</Text>
217+
</View>
218+
))}
219+
</View>
220+
221+
{/* FAB */}
222+
<TouchableOpacity
223+
onPress={() => router.push('/trail-conditions/report')}
224+
className="absolute bottom-6 right-6 h-14 w-14 items-center justify-center rounded-full bg-primary shadow-lg"
225+
>
226+
<Icon name="plus" size={28} color="white" />
227+
</TouchableOpacity>
228+
</View>
229+
</>
230+
);
231+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { LargeTitleHeader } from '@packrat/ui/nativewindui';
2+
import { TrailConditionReportForm, useCurrentLocation } from 'expo-app/features/trail-conditions';
3+
import { router } from 'expo-router';
4+
import { View } from 'react-native';
5+
6+
export default function ReportTrailConditionScreen() {
7+
const { location, isLoading: isLocationLoading } = useCurrentLocation();
8+
9+
return (
10+
<>
11+
<LargeTitleHeader title="Report Condition" />
12+
<View className="flex-1">
13+
{isLocationLoading ? (
14+
<View className="flex-1 items-center justify-center">
15+
<Text className="text-muted-foreground">Getting your location...</Text>
16+
</View>
17+
) : (
18+
<TrailConditionReportForm
19+
initialLocation={
20+
location
21+
? {
22+
latitude: location.coords.latitude,
23+
longitude: location.coords.longitude,
24+
}
25+
: undefined
26+
}
27+
onSuccess={() => router.back()}
28+
onCancel={() => router.back()}
29+
/>
30+
)}
31+
</View>
32+
</>
33+
);
34+
}

0 commit comments

Comments
 (0)