diff --git a/apps/mobile/.gitignore b/apps/mobile/.gitignore new file mode 100644 index 0000000..f1d1d0a --- /dev/null +++ b/apps/mobile/.gitignore @@ -0,0 +1,20 @@ + +# @generated expo-cli sync-2b81b286409207a5da26e14c78851eb30d8ccbdb +# The following patterns were generated by expo-cli + +expo-env.d.ts +# @end expo-cli + +# Expo build artifacts +dist/ +.expo/ + +# Node modules +node_modules/ + +# macOS metadata files (created on external drives) +._* +.DS_Store + +# Environment — never commit secrets +.env \ No newline at end of file diff --git a/apps/mobile/app.json b/apps/mobile/app.json index 7f57280..f44bf2c 100644 --- a/apps/mobile/app.json +++ b/apps/mobile/app.json @@ -56,7 +56,9 @@ "icon": "./assets/images/notification-icon.png", "color": "#0E3D1F" } - ] + ], + "expo-secure-store", + "@react-native-community/datetimepicker" ], "experiments": { "typedRoutes": true diff --git a/apps/mobile/app/(auth)/_layout.tsx b/apps/mobile/app/(auth)/_layout.tsx index 0973bc6..67e81ce 100644 --- a/apps/mobile/app/(auth)/_layout.tsx +++ b/apps/mobile/app/(auth)/_layout.tsx @@ -2,7 +2,8 @@ import { Stack } from 'expo-router'; export default function AuthLayout() { return ( - + + diff --git a/apps/mobile/app/(auth)/login.tsx b/apps/mobile/app/(auth)/login.tsx index 2fdd214..5296c01 100644 --- a/apps/mobile/app/(auth)/login.tsx +++ b/apps/mobile/app/(auth)/login.tsx @@ -1,27 +1,204 @@ -import { View, Text, StyleSheet } from 'react-native'; -import { Link } from 'expo-router'; +import { useState } from 'react'; +import { + View, Text, TextInput, TouchableOpacity, + StyleSheet, KeyboardAvoidingView, Platform, + ScrollView, ActivityIndicator, StatusBar, +} from 'react-native'; +import { useRouter } from 'expo-router'; +import { LinearGradient } from 'expo-linear-gradient'; +import { MaterialIcons } from '@expo/vector-icons'; +import { Colors, Radii } from '@/constants/theme'; + +function SocialBtn({ icon, label }: { icon: React.ComponentProps['name']; label: string }) { + return ( + + + {label} + + ); +} -/** - * Login Screen - * TODO: Implement Clerk sign-in flow with OTP/email - */ export default function LoginScreen() { + const router = useRouter(); + + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [showPass, setShowPass] = useState(false); + const [loading, setLoading] = useState(false); + + const handleLogin = () => { + setLoading(true); + // Simulate brief loading then navigate + setTimeout(() => { + setLoading(false); + router.replace('/(tabs)/dashboard'); + }, 600); + }; + return ( - - 🌾 Agronavis - Login Screen - Clerk sign-in integration coming here - - → Go to Dashboard (dev bypass) - - + + + + + {/* Header */} + + Agronavis + The Precision Horizon + + + {/* Email */} + + Email + + + + + + + {/* Password */} + + + Password + + Forgot Password? + + + + + + setShowPass(p => !p)}> + + + + + + {/* Submit */} + + + {loading + ? + : Log In} + + + + {/* Divider */} + + + Or continue with + + + + {/* Social stubs */} + + + + + + {/* Footer */} + + Don't have an account? + router.push('/(auth)/register')}> + Sign up + + + + + ); } const styles = StyleSheet.create({ - container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#0E3D1F', gap: 12 }, - title: { fontSize: 32, fontWeight: '700', color: '#FFFFFF' }, - subtitle: { fontSize: 18, color: '#A8D5B5' }, - note: { fontSize: 12, color: '#6B9F7E' }, - link: { marginTop: 20, color: '#4CAF50', fontSize: 16 }, + root: { flex: 1, backgroundColor: Colors.surface }, + scroll: { flexGrow: 1, justifyContent: 'center', padding: 24 }, + card: { + backgroundColor: Colors.surfaceContainerLowest, + borderRadius: Radii.xxl, + padding: 28, + gap: 20, + shadowColor: '#0b1c30', + shadowOffset: { width: 0, height: 12 }, + shadowOpacity: 0.06, + shadowRadius: 32, + elevation: 4, + }, + header: { alignItems: 'center', gap: 4 }, + brand: { fontSize: 28, fontWeight: '900', letterSpacing: -0.8, color: Colors.primary }, + subtitle: { fontSize: 14, fontWeight: '500', color: Colors.onSurfaceVariant }, + fieldGroup: { gap: 6 }, + label: { fontSize: 13, fontWeight: '600', color: Colors.onSurface }, + labelRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }, + forgotLink: { fontSize: 13, fontWeight: '600', color: Colors.primary }, + inputWrap: { + flexDirection: 'row', + alignItems: 'center', + backgroundColor: Colors.surfaceContainerHighest, + borderRadius: Radii.lg, + height: 56, + paddingHorizontal: 14, + }, + inputIcon: { marginRight: 10 }, + input: { flex: 1, fontSize: 15, fontWeight: '400', color: Colors.onSurface }, + eyeBtn: { padding: 4 }, + submitBtn: { + height: 56, + borderRadius: Radii.xxl, + alignItems: 'center', + justifyContent: 'center', + shadowColor: Colors.primary, + shadowOffset: { width: 0, height: 8 }, + shadowOpacity: 0.3, + shadowRadius: 16, + elevation: 5, + }, + submitText: { fontSize: 17, fontWeight: '700', color: Colors.onPrimary, letterSpacing: 0.2 }, + divider: { flexDirection: 'row', alignItems: 'center', gap: 10 }, + dividerLine: { flex: 1, height: 1, backgroundColor: Colors.outlineVariant, opacity: 0.5 }, + dividerLabel: { fontSize: 13, color: Colors.onSurfaceVariant, fontWeight: '500' }, + socials: { gap: 10 }, + socialBtn: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + gap: 10, + height: 52, + borderRadius: Radii.lg, + backgroundColor: Colors.surfaceContainerHigh, + }, + socialLabel: { fontSize: 15, fontWeight: '600', color: Colors.onSurface }, + footer: { flexDirection: 'row', justifyContent: 'center', alignItems: 'center', paddingTop: 4 }, + footerText: { fontSize: 14, color: Colors.onSurfaceVariant }, + footerLink: { fontSize: 14, fontWeight: '700', color: Colors.primary }, }); diff --git a/apps/mobile/app/(auth)/register.tsx b/apps/mobile/app/(auth)/register.tsx new file mode 100644 index 0000000..8ed740a --- /dev/null +++ b/apps/mobile/app/(auth)/register.tsx @@ -0,0 +1,207 @@ +import { useState } from 'react'; +import { + View, Text, TextInput, TouchableOpacity, StyleSheet, + KeyboardAvoidingView, Platform, ScrollView, + ActivityIndicator, StatusBar, +} from 'react-native'; +import { useRouter } from 'expo-router'; +import { LinearGradient } from 'expo-linear-gradient'; +import { MaterialIcons } from '@expo/vector-icons'; +import { Colors, Radii } from '@/constants/theme'; + +export default function RegisterScreen() { + const router = useRouter(); + + const [fullName, setFullName] = useState(''); + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [showPass, setShowPass] = useState(false); + const [agreed, setAgreed] = useState(false); + const [loading, setLoading] = useState(false); + + const handleRegister = () => { + if (!agreed) return; + setLoading(true); + setTimeout(() => { + setLoading(false); + router.push('/(onboarding)/step1' as any); + }, 600); + }; + + return ( + + + + + {/* Header */} + + Agronavis + Create Account + Join the precision horizon. + + + {/* Full Name */} + + Full Name + + + + + + + {/* Email */} + + Email + + + + + + + {/* Password */} + + Password + + + + setShowPass(p => !p)}> + + + + + + {/* Terms */} + + setAgreed(a => !a)} + activeOpacity={0.8} + > + {agreed && } + + + I agree to the{' '} + Terms and Conditions + + + + {/* Submit */} + + + {loading + ? + : Create Account} + + + + {/* Footer */} + + Already have an account? + router.push('/(auth)/login')}> + Log in + + + + + + ); +} + +const styles = StyleSheet.create({ + root: { flex: 1, backgroundColor: Colors.surface }, + scroll: { flexGrow: 1, justifyContent: 'center', padding: 24 }, + card: { + backgroundColor: Colors.surfaceContainerLow, + borderRadius: Radii.xl, + padding: 28, + gap: 18, + shadowColor: '#0b1c30', + shadowOffset: { width: 0, height: 12 }, + shadowOpacity: 0.06, + shadowRadius: 32, + elevation: 4, + }, + header: { alignItems: 'center', gap: 4 }, + brand: { fontSize: 26, fontWeight: '900', letterSpacing: -0.8, color: Colors.primary }, + title: { fontSize: 26, fontWeight: '700', letterSpacing: -0.5, color: Colors.onSurface, marginTop: 4 }, + subtitle: { fontSize: 14, fontWeight: '400', color: Colors.onSurfaceVariant }, + fieldGroup: { gap: 6 }, + label: { fontSize: 13, fontWeight: '600', color: Colors.onSurfaceVariant }, + inputWrap: { + flexDirection: 'row', + alignItems: 'center', + backgroundColor: Colors.surfaceContainerHighest, + borderRadius: Radii.lg, + height: 52, + paddingHorizontal: 14, + }, + icon: { marginRight: 10 }, + input: { flex: 1, fontSize: 15, fontWeight: '400', color: Colors.onSurface }, + eyeBtn: { padding: 4 }, + terms: { flexDirection: 'row', alignItems: 'center', gap: 10 }, + checkbox: { + width: 22, height: 22, borderRadius: 6, borderWidth: 2, + borderColor: Colors.outlineVariant, alignItems: 'center', justifyContent: 'center', + backgroundColor: Colors.surfaceContainerHighest, + }, + checkboxChecked: { backgroundColor: Colors.primary, borderColor: Colors.primary }, + termsText: { flex: 1, fontSize: 13, color: Colors.onSurfaceVariant, lineHeight: 20 }, + termsLink: { fontWeight: '600', color: Colors.primary }, + submitBtn: { + height: 56, + borderRadius: Radii.xxl, + alignItems: 'center', + justifyContent: 'center', + shadowColor: Colors.primary, + shadowOffset: { width: 0, height: 8 }, + shadowOpacity: 0.3, + shadowRadius: 16, + elevation: 5, + }, + submitText: { fontSize: 17, fontWeight: '700', color: Colors.onPrimary, letterSpacing: 0.2 }, + footer: { flexDirection: 'row', justifyContent: 'center', alignItems: 'center', paddingTop: 4 }, + footerText: { fontSize: 14, color: Colors.onSurfaceVariant }, + footerLink: { fontSize: 14, fontWeight: '700', color: Colors.primary }, +}); diff --git a/apps/mobile/app/(auth)/verify.tsx b/apps/mobile/app/(auth)/verify.tsx new file mode 100644 index 0000000..74156ea --- /dev/null +++ b/apps/mobile/app/(auth)/verify.tsx @@ -0,0 +1,22 @@ +import { useEffect } from 'react'; +import { View, ActivityIndicator, StyleSheet } from 'react-native'; +import { useRouter } from 'expo-router'; +import { Colors } from '@/constants/theme'; + +// Verification is handled server-side — this screen redirects to dashboard. +export default function VerifyScreen() { + const router = useRouter(); + useEffect(() => { + const t = setTimeout(() => router.replace('/(tabs)/dashboard'), 500); + return () => clearTimeout(t); + }, []); + return ( + + + + ); +} + +const styles = StyleSheet.create({ + root: { flex: 1, alignItems: 'center', justifyContent: 'center', backgroundColor: Colors.surface }, +}); diff --git a/apps/mobile/app/(auth)/welcome.tsx b/apps/mobile/app/(auth)/welcome.tsx new file mode 100644 index 0000000..357d14d --- /dev/null +++ b/apps/mobile/app/(auth)/welcome.tsx @@ -0,0 +1,141 @@ +import { useRouter } from 'expo-router'; +import { LinearGradient } from 'expo-linear-gradient'; +import { ImageBackground, StyleSheet, Text, View, TouchableOpacity, StatusBar } from 'react-native'; + +import { MaterialIcons } from '@expo/vector-icons'; +import { Colors, Radii } from '@/constants/theme'; + +const FIELD_IMAGE = { uri: 'https://images.unsplash.com/photo-1464226184884-fa280b87c399?w=800&q=80' }; + +export default function WelcomeScreen() { + const router = useRouter(); + + // Micro-animation for primary CTA + + + return ( + + + + {/* Background field photo */} + + + {/* Gradient scrim — bottom-heavy to ensure readability */} + + + {/* Content */} + + {/* Logo + wordmark */} + + + + + Agronavis + The precision horizon for modern agriculture. + + + {/* CTA buttons */} + + {/* Get Started */} + + router.push('/(auth)/register')} + > + + Get Started + + + + + {/* Log In */} + + router.push('/(auth)/login')} + style={styles.btnSecondary} + > + Log In + + + + + + ); +} + +const styles = StyleSheet.create({ + root: { flex: 1, backgroundColor: Colors.surface }, + content: { + flex: 1, + justifyContent: 'flex-end', + alignItems: 'center', + paddingHorizontal: 24, + paddingBottom: 56, + gap: 48, + }, + brand: { alignItems: 'center', gap: 12 }, + iconBadge: { + width: 80, + height: 80, + borderRadius: Radii.xl, + backgroundColor: 'rgba(248,249,255,0.85)', + alignItems: 'center', + justifyContent: 'center', + shadowColor: '#0b1c30', + shadowOffset: { width: 0, height: 12 }, + shadowOpacity: 0.12, + shadowRadius: 24, + elevation: 8, + }, + appName: { + fontSize: 52, + fontWeight: '900', + letterSpacing: -1.5, + color: Colors.onSurface, + }, + tagline: { + fontSize: 16, + fontWeight: '400', + color: Colors.onSurfaceVariant, + textAlign: 'center', + lineHeight: 24, + maxWidth: 260, + }, + actions: { width: '100%', gap: 12 }, + btnPrimary: { + width: '100%', + height: 56, + borderRadius: Radii.xl, + alignItems: 'center', + justifyContent: 'center', + shadowColor: Colors.primary, + shadowOffset: { width: 0, height: 8 }, + shadowOpacity: 0.35, + shadowRadius: 16, + elevation: 6, + }, + btnPrimaryText: { fontSize: 17, fontWeight: '700', color: Colors.onPrimary, letterSpacing: 0.2 }, + btnSecondary: { + width: '100%', + height: 56, + borderRadius: Radii.xl, + alignItems: 'center', + justifyContent: 'center', + backgroundColor: Colors.surfaceContainerHigh, + }, + btnSecondaryText: { fontSize: 17, fontWeight: '700', color: Colors.onSurface, letterSpacing: 0.2 }, +}); diff --git a/apps/mobile/app/(onboarding)/_layout.tsx b/apps/mobile/app/(onboarding)/_layout.tsx index 5a52333..8ddb22d 100644 --- a/apps/mobile/app/(onboarding)/_layout.tsx +++ b/apps/mobile/app/(onboarding)/_layout.tsx @@ -1,12 +1,5 @@ import { Stack } from 'expo-router'; export default function OnboardingLayout() { - return ( - - - - - - - ); + return ; } diff --git a/apps/mobile/app/(onboarding)/step1.tsx b/apps/mobile/app/(onboarding)/step1.tsx new file mode 100644 index 0000000..1717573 --- /dev/null +++ b/apps/mobile/app/(onboarding)/step1.tsx @@ -0,0 +1,175 @@ +import { useState } from 'react'; +import { + View, Text, TouchableOpacity, StyleSheet, + ScrollView, StatusBar, Image, +} from 'react-native'; +import { useRouter } from 'expo-router'; +import { LinearGradient } from 'expo-linear-gradient'; +import { MaterialIcons } from '@expo/vector-icons'; +import { Colors, Radii } from '@/constants/theme'; + +const CROPS = ['Wheat', 'Rice', 'Corn', 'Soybeans', 'Cotton', 'Sugarcane', 'Other']; + +export default function OnboardingStep1() { + const router = useRouter(); + const [selectedCrop, setSelectedCrop] = useState(''); + const [farmSize, setFarmSizeIdx] = useState(2); // index into size options + + const SIZE_LABELS = ['< 1 Acre', '1–5 Acres', '5–25 Acres', '25–100 Acres', '100+ Acres']; + + return ( + + + + {/* Header */} + + Agronavis + + Step 1 of 3 + + {[0, 1, 2].map(i => ( + + ))} + + + + + + {/* Profile avatar placeholder */} + + + + + + + + Upload Profile Photo + + + {/* Title */} + Welcome to the Field + + Let's set up your profile to calibrate our systems for your operation. + + + {/* Primary Crop */} + + Primary Crop Focus + + {CROPS.map(crop => ( + setSelectedCrop(crop)} + activeOpacity={0.8} + > + + {crop} + + + ))} + + + + {/* Farm Size */} + + Operation Scale + + {SIZE_LABELS.map((label, i) => ( + setFarmSizeIdx(i)} + activeOpacity={0.8} + > + + {label} + + + ))} + + + + + {/* Action */} + + router.push('/(onboarding)/step2' as any)} + activeOpacity={0.88} + > + + Next Step + + + + + + ); +} + +const styles = StyleSheet.create({ + root: { flex: 1, backgroundColor: Colors.surface }, + header: { + flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', + paddingHorizontal: 24, paddingTop: 56, paddingBottom: 16, + backgroundColor: 'rgba(248,249,255,0.9)', + }, + brand: { fontSize: 22, fontWeight: '900', color: Colors.primary, letterSpacing: -0.5 }, + progressRow: { alignItems: 'flex-end', gap: 6 }, + stepLabel: { fontSize: 13, fontWeight: '600', color: Colors.primary }, + dots: { flexDirection: 'row', gap: 6 }, + dot: { width: 28, height: 7, borderRadius: 4, backgroundColor: Colors.surfaceContainerHighest }, + dotActive: { backgroundColor: Colors.primary }, + scroll: { paddingHorizontal: 24, paddingBottom: 24 }, + avatarWrap: { alignItems: 'center', marginTop: 24, marginBottom: 28, gap: 8 }, + avatarCircle: { + width: 108, height: 108, borderRadius: 54, + backgroundColor: Colors.surfaceContainerHigh, + alignItems: 'center', justifyContent: 'center', + shadowColor: '#0b1c30', shadowOffset: { width: 0, height: 8 }, + shadowOpacity: 0.07, shadowRadius: 16, elevation: 4, + }, + avatarBadge: { + position: 'absolute', bottom: 28, right: '30%', + width: 32, height: 32, borderRadius: 16, + backgroundColor: Colors.primaryContainer, + alignItems: 'center', justifyContent: 'center', + shadowColor: '#0b1c30', shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.15, shadowRadius: 8, elevation: 4, + }, + avatarHint: { fontSize: 13, fontWeight: '500', color: Colors.onSurfaceVariant }, + title: { fontSize: 30, fontWeight: '800', letterSpacing: -0.6, color: Colors.onSurface, textAlign: 'center', marginBottom: 8 }, + subtitle: { fontSize: 15, color: Colors.onSurfaceVariant, textAlign: 'center', lineHeight: 22, marginBottom: 28 }, + section: { gap: 12, marginBottom: 24 }, + sectionLabel: { fontSize: 13, fontWeight: '700', color: Colors.onSurface, textTransform: 'uppercase', letterSpacing: 0.8 }, + cropScroll: { marginHorizontal: -4 }, + cropChip: { + paddingHorizontal: 18, paddingVertical: 10, borderRadius: Radii.full, + backgroundColor: Colors.surfaceContainerHigh, marginHorizontal: 4, + }, + cropChipActive: { backgroundColor: Colors.primary }, + cropChipText: { fontSize: 14, fontWeight: '600', color: Colors.onSurface }, + cropChipTextActive: { color: '#fff' }, + sizeRow: { flexDirection: 'row', flexWrap: 'wrap', gap: 8 }, + sizeChip: { + paddingHorizontal: 14, paddingVertical: 8, borderRadius: Radii.lg, + backgroundColor: Colors.surfaceContainerHigh, + }, + sizeChipActive: { backgroundColor: Colors.primaryFixed }, + sizeChipText: { fontSize: 13, fontWeight: '500', color: Colors.onSurface }, + sizeChipTextActive: { fontWeight: '700', color: Colors.primary }, + footer: { padding: 24, paddingBottom: 40 }, + nextBtn: { borderRadius: Radii.xxl, overflow: 'hidden' }, + nextGradient: { + flexDirection: 'row', alignItems: 'center', justifyContent: 'center', + height: 56, gap: 8, + shadowColor: Colors.primary, shadowOffset: { width: 0, height: 8 }, + shadowOpacity: 0.3, shadowRadius: 16, elevation: 6, + }, + nextText: { fontSize: 17, fontWeight: '700', color: '#fff', letterSpacing: 0.2 }, +}); diff --git a/apps/mobile/app/(onboarding)/step2.tsx b/apps/mobile/app/(onboarding)/step2.tsx new file mode 100644 index 0000000..0d54fc6 --- /dev/null +++ b/apps/mobile/app/(onboarding)/step2.tsx @@ -0,0 +1,142 @@ +import { View, Text, TouchableOpacity, StyleSheet, StatusBar, Image } from 'react-native'; +import { useRouter } from 'expo-router'; +import { LinearGradient } from 'expo-linear-gradient'; +import { MaterialIcons } from '@expo/vector-icons'; +import { Colors, Radii } from '@/constants/theme'; + +export default function OnboardingStep2() { + const router = useRouter(); + + return ( + + + + {/* Header */} + + router.back()} style={styles.backBtn}> + + + Agronavis + + + + {/* Progress */} + + + Step 2 of 3 + Location + + + + + + + {/* Main */} + + Where is your primary field? + + We use this to fetch localized weather data and soil telemetry. + + + {/* Search bar (visual) */} + + + Enter farm address or coordinates + + + {/* Location button */} + + + Use Current Location + + + {/* Map preview */} + + + + + + + + Tap to select location + + + + + {/* Footer */} + + router.push('/(onboarding)/step3' as any)} + activeOpacity={0.88} + > + + Next + + + + + + ); +} + +const styles = StyleSheet.create({ + root: { flex: 1, backgroundColor: Colors.surface }, + header: { + flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', + paddingHorizontal: 20, paddingTop: 56, paddingBottom: 12, + }, + backBtn: { padding: 8, borderRadius: Radii.full, backgroundColor: Colors.surfaceContainerHigh }, + brand: { fontSize: 20, fontWeight: '900', color: Colors.primary }, + w10: { width: 38 }, + progressWrap: { paddingHorizontal: 24, marginBottom: 8 }, + progressRow: { flexDirection: 'row', justifyContent: 'space-between', marginBottom: 8 }, + stepLabel: { fontSize: 13, fontWeight: '600', color: Colors.onSurfaceVariant }, + stepSub: { fontSize: 13, fontWeight: '600', color: Colors.onSurfaceVariant }, + progressTrack: { height: 8, backgroundColor: Colors.surfaceContainerHigh, borderRadius: 4, overflow: 'hidden' }, + progressFill: { width: '66.66%', height: '100%', borderRadius: 4 }, + content: { flex: 1, paddingHorizontal: 24, gap: 14 }, + title: { fontSize: 26, fontWeight: '800', letterSpacing: -0.5, color: Colors.onSurface }, + subtitle: { fontSize: 15, color: Colors.onSurfaceVariant, lineHeight: 22 }, + searchBar: { + flexDirection: 'row', alignItems: 'center', gap: 10, + backgroundColor: Colors.surfaceContainerHighest, + borderRadius: Radii.lg, height: 52, paddingHorizontal: 16, + }, + searchPlaceholder: { fontSize: 15, color: Colors.outline, flex: 1 }, + locationBtn: { + flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 10, + backgroundColor: Colors.surfaceContainerHigh, borderRadius: Radii.lg, height: 52, + }, + locationText: { fontSize: 15, fontWeight: '600', color: Colors.onSurface }, + mapWrap: { flex: 1, borderRadius: Radii.xl, overflow: 'hidden', minHeight: 200 }, + mapImage: { width: '100%', height: '100%' }, + mapOverlay: { ...StyleSheet.absoluteFillObject, backgroundColor: 'rgba(0,0,0,0.15)' }, + pinWrap: { + position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, + alignItems: 'center', justifyContent: 'center', + }, + mapLabel: { + position: 'absolute', bottom: 16, left: 16, right: 16, + backgroundColor: 'rgba(255,255,255,0.85)', borderRadius: Radii.lg, + paddingVertical: 10, paddingHorizontal: 16, alignItems: 'center', + }, + mapLabelText: { fontSize: 14, fontWeight: '600', color: Colors.onSurface }, + footer: { padding: 24, paddingBottom: 40 }, + nextGradient: { + flexDirection: 'row', alignItems: 'center', justifyContent: 'center', + height: 56, borderRadius: Radii.xxl, gap: 8, + }, + nextText: { fontSize: 17, fontWeight: '700', color: '#fff' }, +}); diff --git a/apps/mobile/app/(onboarding)/step3.tsx b/apps/mobile/app/(onboarding)/step3.tsx new file mode 100644 index 0000000..0aa308f --- /dev/null +++ b/apps/mobile/app/(onboarding)/step3.tsx @@ -0,0 +1,172 @@ +import { useState } from 'react'; +import { + View, Text, TouchableOpacity, StyleSheet, + StatusBar, Switch, +} from 'react-native'; +import { useRouter } from 'expo-router'; +import { LinearGradient } from 'expo-linear-gradient'; +import { MaterialIcons } from '@expo/vector-icons'; +import { Colors, Radii } from '@/constants/theme'; + +type NotifItem = { + id: string; + icon: React.ComponentProps['name']; + title: string; + desc: string; +}; + +const NOTIFICATIONS: NotifItem[] = [ + { id: 'weather', icon: 'wb-sunny', title: 'Weather Alerts', desc: 'Frost, storm, and rain warnings for your region' }, + { id: 'tasks', icon: 'event-note', title: 'Task Reminders', desc: 'Irrigation, fertilizer, and harvest schedules' }, + { id: 'pests', icon: 'bug-report', title: 'Pest & Disease', desc: 'Early warnings from your community network' }, + { id: 'market', icon: 'trending-up', title: 'Market Prices', desc: 'Live mandi price updates for your crops' }, +]; + +export default function OnboardingStep3() { + const router = useRouter(); + const [enabled, setEnabled] = useState>({ + weather: true, tasks: true, pests: false, market: false, + }); + + const toggle = (id: string) => setEnabled(prev => ({ ...prev, [id]: !prev[id] })); + + return ( + + + + {/* Header */} + + router.back()} style={styles.backBtn}> + + + Agronavis + + + + {/* Progress */} + + + Step 3 of 3 + Notifications + + + + + + + {/* Content */} + + + + + Stay Informed + + Choose which alerts you want to receive. You can always change this later in Settings. + + + + {NOTIFICATIONS.map(item => ( + + + + + + {item.title} + {item.desc} + + toggle(item.id)} + trackColor={{ false: Colors.surfaceContainerHigh, true: Colors.primaryFixed }} + thumbColor={enabled[item.id] ? Colors.primary : Colors.outline} + /> + + ))} + + + + {/* Footer */} + + router.replace('/(tabs)/dashboard')} + activeOpacity={0.88} + > + + Get Started + + + + router.replace('/(tabs)/dashboard')} style={styles.skipBtn}> + Skip for now + + + + ); +} + +const styles = StyleSheet.create({ + root: { flex: 1, backgroundColor: Colors.surface }, + header: { + flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', + paddingHorizontal: 20, paddingTop: 56, paddingBottom: 12, + }, + backBtn: { padding: 8, borderRadius: Radii.full, backgroundColor: Colors.surfaceContainerHigh }, + brand: { fontSize: 20, fontWeight: '900', color: Colors.primary }, + w38: { width: 38 }, + progressWrap: { paddingHorizontal: 24, marginBottom: 8 }, + progressRow: { flexDirection: 'row', justifyContent: 'space-between', marginBottom: 8 }, + stepLabel: { fontSize: 13, fontWeight: '600', color: Colors.onSurfaceVariant }, + stepSub: { fontSize: 13, fontWeight: '600', color: Colors.onSurfaceVariant }, + progressTrack: { height: 8, backgroundColor: Colors.surfaceContainerHigh, borderRadius: 4, overflow: 'hidden' }, + progressFill: { width: '100%', height: '100%', borderRadius: 4 }, + content: { flex: 1, paddingHorizontal: 24 }, + iconWrap: { + width: 88, height: 88, borderRadius: Radii.xl, + backgroundColor: Colors.primaryFixed, + alignItems: 'center', justifyContent: 'center', + alignSelf: 'center', marginTop: 16, marginBottom: 16, + shadowColor: Colors.primary, shadowOffset: { width: 0, height: 8 }, + shadowOpacity: 0.2, shadowRadius: 16, elevation: 4, + }, + title: { fontSize: 26, fontWeight: '800', letterSpacing: -0.5, color: Colors.onSurface, textAlign: 'center', marginBottom: 8 }, + subtitle: { fontSize: 14, color: Colors.onSurfaceVariant, textAlign: 'center', lineHeight: 22, marginBottom: 24 }, + list: { gap: 12 }, + row: { + flexDirection: 'row', alignItems: 'center', gap: 14, + backgroundColor: Colors.surfaceContainerLowest, borderRadius: Radii.xl, + padding: 16, + shadowColor: '#0b1c30', shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.04, shadowRadius: 12, elevation: 2, + }, + iconBox: { + width: 44, height: 44, borderRadius: Radii.md, + backgroundColor: Colors.surfaceContainerHigh, + alignItems: 'center', justifyContent: 'center', + }, + iconBoxActive: { backgroundColor: Colors.primaryFixed }, + rowText: { flex: 1 }, + rowTitle: { fontSize: 15, fontWeight: '700', color: Colors.onSurface }, + rowDesc: { fontSize: 12, color: Colors.onSurfaceVariant, marginTop: 2, lineHeight: 16 }, + footer: { padding: 24, paddingBottom: 40, gap: 12 }, + finishGradient: { + flexDirection: 'row', alignItems: 'center', justifyContent: 'center', + height: 56, borderRadius: Radii.xxl, gap: 8, + shadowColor: Colors.primary, shadowOffset: { width: 0, height: 8 }, + shadowOpacity: 0.3, shadowRadius: 16, elevation: 6, + }, + finishText: { fontSize: 17, fontWeight: '700', color: '#fff' }, + skipBtn: { alignItems: 'center', paddingVertical: 8 }, + skipText: { fontSize: 14, color: Colors.outline, fontWeight: '500' }, +}); diff --git a/apps/mobile/app/(tabs)/_layout.tsx b/apps/mobile/app/(tabs)/_layout.tsx index c1794f5..ec1d6d8 100644 --- a/apps/mobile/app/(tabs)/_layout.tsx +++ b/apps/mobile/app/(tabs)/_layout.tsx @@ -1,21 +1,15 @@ import { Tabs } from 'expo-router'; -import { Ionicons } from '@expo/vector-icons'; +import { MaterialIcons } from '@expo/vector-icons'; +import { Colors } from '@/constants/theme'; -type IconName = React.ComponentProps['name']; +type IconName = React.ComponentProps['name']; -interface TabConfig { - name: string; - title: string; - icon: IconName; - activeIcon: IconName; -} - -const TABS: TabConfig[] = [ - { name: 'dashboard', title: 'Home', icon: 'home-outline', activeIcon: 'home' }, - { name: 'farm', title: 'Farm', icon: 'leaf-outline', activeIcon: 'leaf' }, - { name: 'crops', title: 'Crops', icon: 'nutrition-outline', activeIcon: 'nutrition' }, - { name: 'advisory', title: 'Advisory', icon: 'bulb-outline', activeIcon: 'bulb' }, - { name: 'community', title: 'Community', icon: 'people-outline', activeIcon: 'people' }, +// Exactly 4 tabs — no more, no less. +const TABS: { name: string; title: string; icon: IconName }[] = [ + { name: 'dashboard/index', title: 'Dashboard', icon: 'dashboard' }, + { name: 'farm', title: 'My Farms', icon: 'agriculture' }, + { name: 'scan', title: 'AI Scanner',icon: 'photo-camera' }, + { name: 'community/index', title: 'Community', icon: 'groups' }, ]; export default function TabsLayout() { @@ -24,24 +18,38 @@ export default function TabsLayout() { screenOptions={{ headerShown: false, tabBarStyle: { - backgroundColor: '#0E3D1F', - borderTopColor: '#1A5C30', - height: 65, - paddingBottom: 10, + backgroundColor: 'rgba(255,255,255,0.96)', + borderTopWidth: 0, + height: 80, + paddingBottom: 20, + paddingTop: 10, + borderTopLeftRadius: 28, + borderTopRightRadius: 28, + shadowColor: '#0b1c30', + shadowOffset: { width: 0, height: -6 }, + shadowOpacity: 0.08, + shadowRadius: 20, + elevation: 20, + position: 'absolute', + }, + tabBarActiveTintColor: Colors.primary, + tabBarInactiveTintColor: Colors.outline, + tabBarLabelStyle: { + fontSize: 10, + fontWeight: '700', + letterSpacing: 0.3, + marginTop: 2, }, - tabBarActiveTintColor: '#4CAF50', - tabBarInactiveTintColor: '#6B9F7E', - tabBarLabelStyle: { fontSize: 11, fontWeight: '600' }, }} > - {TABS.map(({ name, title, icon, activeIcon }) => ( + {TABS.map(({ name, title, icon }) => ( ( - + tabBarIcon: ({ color, size }) => ( + ), }} /> diff --git a/apps/mobile/app/(tabs)/advisory/index.tsx b/apps/mobile/app/(tabs)/advisory/index.tsx deleted file mode 100644 index c1e23c1..0000000 --- a/apps/mobile/app/(tabs)/advisory/index.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { View, Text, StyleSheet } from 'react-native'; - -/** - * Advisory Screen - * TODO: AI-generated advisories, weather alerts, pest warnings, scheme info - */ -export default function AdvisoryScreen() { - return ( - - Advisory - AI farm advisories coming soon - - ); -} - -const styles = StyleSheet.create({ - container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#0A2B15' }, - title: { fontSize: 28, fontWeight: '700', color: '#FFFFFF' }, - subtitle: { fontSize: 14, color: '#6B9F7E', marginTop: 8 }, -}); diff --git a/apps/mobile/app/(tabs)/community/index.tsx b/apps/mobile/app/(tabs)/community/index.tsx index 478fdc6..1d8db39 100644 --- a/apps/mobile/app/(tabs)/community/index.tsx +++ b/apps/mobile/app/(tabs)/community/index.tsx @@ -1,20 +1,195 @@ -import { View, Text, StyleSheet } from 'react-native'; +import { + View, Text, ScrollView, TouchableOpacity, + StyleSheet, StatusBar, Image, +} from 'react-native'; +import { useRouter } from 'expo-router'; +import { MaterialIcons } from '@expo/vector-icons'; +import { Colors, Radii } from '@/constants/theme'; + +const AVATAR_URL = 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=200&q=80'; + +const POSTS = [ + { + id: '1', + name: 'Rajesh Mishra', + location: 'Bilaspur • 2h ago', + avatar: 'https://images.unsplash.com/photo-1552058544-f2b08422138a?w=200&q=70', + content: '"Aphid attack spotted in neighboring village, check your crops."', + img: 'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=600&q=70', + tag: 'Urgent Alert', + tagColor: Colors.errorContainer, + likes: 24, + replies: 12, + }, + { + id: '2', + name: 'Sunita Kaur', + location: 'Faridkot • 5h ago', + avatar: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=200&q=70', + content: 'New irrigation system installed today! The water pressure is significantly better. Anyone else using the solar-pump subsidy?', + img: null, + tag: null, + tagColor: '', + likes: 0, + replies: 8, + }, +]; + +const TICKER = ['Wheat ₹2,275/q ▲', 'Rice (Basmati) ₹4,850/q ▼', 'Cotton ₹7,120/q ▲', 'Soybean ₹4,400/q ●', 'Maize ₹1,950/q ▲']; -/** - * Community Screen - * TODO: Farmer posts, knowledge sharing, Q&A, market insights - */ export default function CommunityScreen() { + const router = useRouter(); + return ( - - Community - Farmer community coming soon + + + + {/* Mandi ticker */} + + + Live Mandi + + + {TICKER.map(t => ( + {t} + ))} + + + + {/* Top Bar */} + + router.push('/profile' as any)} activeOpacity={0.85}> + + + Agronavis + + + + + + + Community Feed + Connect with 1,200 farmers in your local circle. + + {/* Create post */} + + + + + Share an update from your farm... + + + {/* Posts */} + {POSTS.map(post => ( + + + + + {post.name} + {post.location} + + {post.tag && ( + + {post.tag} + + )} + + {post.content} + {post.img && ( + + )} + + + + {post.likes ? `Helpful (${post.likes})` : 'Like'} + + + + {post.replies ? `Comment (${post.replies})` : 'Comment'} + + + + ))} + + {/* Community highlights */} + + + + 94% + Local Sentiment + + + + 15+ + Active Meets + + + + + ); } const styles = StyleSheet.create({ - container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#0A2B15' }, - title: { fontSize: 28, fontWeight: '700', color: '#FFFFFF' }, - subtitle: { fontSize: 14, color: '#6B9F7E', marginTop: 8 }, + root: { flex: 1, backgroundColor: Colors.surface }, + ticker: { + flexDirection: 'row', alignItems: 'center', + backgroundColor: Colors.surfaceContainerHighest, paddingVertical: 8, + }, + tickerBadge: { + backgroundColor: Colors.primary, paddingHorizontal: 12, paddingVertical: 4, + borderTopRightRadius: Radii.full, borderBottomRightRadius: Radii.full, + marginRight: 8, + }, + tickerBadgeText: { fontSize: 10, fontWeight: '800', color: '#fff', letterSpacing: 0.5 }, + tickerItem: { fontSize: 13, fontWeight: '600', color: Colors.onSurfaceVariant, marginHorizontal: 12 }, + topBar: { + flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', + paddingHorizontal: 20, paddingVertical: 12, + backgroundColor: 'rgba(248,249,255,0.92)', + shadowColor: '#0b1c30', shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.06, shadowRadius: 12, elevation: 6, + }, + avatar: { width: 40, height: 40, borderRadius: 20, borderWidth: 2, borderColor: Colors.primaryFixed }, + logo: { fontSize: 22, fontWeight: '900', letterSpacing: -0.8, color: Colors.primary }, + notifBtn: { width: 40, height: 40, alignItems: 'center', justifyContent: 'center' }, + scroll: { paddingHorizontal: 20, paddingTop: 16, gap: 14 }, + pageTitle: { fontSize: 26, fontWeight: '800', letterSpacing: -0.5, color: Colors.onSurface }, + pageSub: { fontSize: 14, color: Colors.onSurfaceVariant }, + createPost: { + flexDirection: 'row', alignItems: 'center', gap: 12, + backgroundColor: Colors.surfaceContainerLowest, borderRadius: Radii.xl, padding: 16, + shadowColor: '#0b1c30', shadowOffset: { width: 0, height: 3 }, + shadowOpacity: 0.04, shadowRadius: 8, elevation: 2, + }, + createIcon: { width: 40, height: 40, borderRadius: 12, backgroundColor: Colors.surfaceContainerHigh, alignItems: 'center', justifyContent: 'center' }, + createText: { fontSize: 14, color: Colors.onSurfaceVariant }, + postCard: { + backgroundColor: Colors.surfaceContainerLowest, borderRadius: Radii.xxl, overflow: 'hidden', + shadowColor: '#0b1c30', shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.05, shadowRadius: 12, elevation: 3, + }, + postHeader: { flexDirection: 'row', alignItems: 'center', gap: 10, padding: 16, paddingBottom: 10 }, + postAvatar: { width: 46, height: 46, borderRadius: 14 }, + postAuthor: { flex: 1 }, + postName: { fontSize: 15, fontWeight: '700', color: Colors.onSurface }, + postLoc: { fontSize: 12, color: Colors.onSurfaceVariant, marginTop: 1 }, + postTag: { borderRadius: Radii.full, paddingHorizontal: 10, paddingVertical: 4 }, + postTagText: { fontSize: 10, fontWeight: '800', color: Colors.error, letterSpacing: 0.4 }, + postContent: { fontSize: 15, color: Colors.onSurfaceVariant, lineHeight: 22, paddingHorizontal: 16, paddingBottom: 12 }, + postImg: { width: '100%', height: 180, marginBottom: 12 }, + postActions: { flexDirection: 'row', borderTopWidth: 1, borderTopColor: Colors.outlineVariant }, + actionBtn: { + flex: 1, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 6, + height: 50, + }, + actionText: { fontSize: 14, fontWeight: '600', color: Colors.onSurface }, + highlightsRow: { flexDirection: 'row', gap: 12 }, + highlightCard: { + flex: 1, borderRadius: Radii.xxl, padding: 20, gap: 6, + aspectRatio: 1, justifyContent: 'flex-end', + }, + highlightNum: { fontSize: 26, fontWeight: '900', color: Colors.onSurface }, + highlightLabel:{ fontSize: 11, fontWeight: '700', color: Colors.onSurfaceVariant, textTransform: 'uppercase', letterSpacing: 0.8 }, }); diff --git a/apps/mobile/app/(tabs)/crops/index.tsx b/apps/mobile/app/(tabs)/crops/index.tsx deleted file mode 100644 index dada951..0000000 --- a/apps/mobile/app/(tabs)/crops/index.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { View, Text, StyleSheet } from 'react-native'; - -/** - * Crops Screen - * TODO: Crop tracking, growth stages, disease alerts, scan integration - */ -export default function CropsScreen() { - return ( - - My Crops - Crop monitoring coming soon - - ); -} - -const styles = StyleSheet.create({ - container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#0A2B15' }, - title: { fontSize: 28, fontWeight: '700', color: '#FFFFFF' }, - subtitle: { fontSize: 14, color: '#6B9F7E', marginTop: 8 }, -}); diff --git a/apps/mobile/app/(tabs)/dashboard/index.tsx b/apps/mobile/app/(tabs)/dashboard/index.tsx index 6d5f3e5..55419b3 100644 --- a/apps/mobile/app/(tabs)/dashboard/index.tsx +++ b/apps/mobile/app/(tabs)/dashboard/index.tsx @@ -1,20 +1,355 @@ -import { View, Text, StyleSheet } from 'react-native'; +import { + View, Text, ScrollView, TouchableOpacity, + StyleSheet, StatusBar, Image, +} from 'react-native'; +import { useRouter } from 'expo-router'; +import { LinearGradient } from 'expo-linear-gradient'; +import { MaterialIcons } from '@expo/vector-icons'; +import { Colors, Radii } from '@/constants/theme'; + +const AVATAR_URL = 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=200&q=80'; +const FARM_URL = 'https://images.unsplash.com/photo-1500382017468-9049fed747ef?w=800&q=80'; + +function StatChip({ label, value, color }: { label: string; value: string; color: string }) { + return ( + + {value} + {label} + + ); +} + +function TaskCard({ + tag, tagColor, title, desc, due, +}: { tag: string; tagColor: string; title: string; desc: string; due: string }) { + return ( + + + + {tag} + + {title} + {desc} + {due} + + + + + + ); +} -/** - * Dashboard Screen - * TODO: Farm overview, weather widget, quick actions, advisory summary - */ export default function DashboardScreen() { + const router = useRouter(); + return ( - - Dashboard - Farm overview coming soon + + + + {/* Top Bar */} + + router.push('/profile' as any)} activeOpacity={0.85}> + + + Agronavis + + + + + + + {/* Greeting + Weather */} + + + Hello, Rajesh 👋 + Thursday, 24 October 2024 + + + + WEATHER + 28°C + Sunny + + + + + + {/* Crop Status */} + + {/* Crop progress */} + + + + + Active Growth + + Wheat (HD 2967) + Day 45 of 120 + + + + + + + + Seeding + Tillering ● + Harvest + + + + {/* Soil moisture */} + + + 64% + + + Soil Moisture + + Level is optimal. Next irrigation in 48h. + + + + + + + + + + {/* NPK Stats */} + + + + + + + {/* Select Crops CTA */} + router.push('/crops')} + activeOpacity={0.88} + style={styles.cropsCta} + > + + + + + Select Your Crops + Customize your dashboard + + + + + + + {/* Aerial Monitoring */} + router.push('/(tabs)/farm')} + activeOpacity={0.9} + > + + + + + LIVE SATELLITE FEED + + + Northern Plains Monitoring + All systems online • 4m ago + + + + + + + {/* Tasks */} + + + {' '}UPCOMING TASKS + + + + + + + + + Tomorrow + Pest Inspection: Sector A + + + + + + + + Oct 26 + Weather Calibration Scan + + + + + + ); } const styles = StyleSheet.create({ - container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#0A2B15' }, - title: { fontSize: 28, fontWeight: '700', color: '#FFFFFF' }, - subtitle: { fontSize: 14, color: '#6B9F7E', marginTop: 8 }, + root: { flex: 1, backgroundColor: Colors.surface }, + topBar: { + flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', + paddingHorizontal: 20, paddingTop: 52, paddingBottom: 14, + backgroundColor: 'rgba(248,249,255,0.92)', + shadowColor: '#0b1c30', shadowOffset: { width: 0, height: 6 }, + shadowOpacity: 0.06, shadowRadius: 16, elevation: 8, + }, + avatar: { width: 40, height: 40, borderRadius: 20, borderWidth: 2, borderColor: Colors.primaryFixed }, + logo: { fontSize: 24, fontWeight: '900', letterSpacing: -0.8, color: Colors.primary }, + notifBtn: { width: 40, height: 40, alignItems: 'center', justifyContent: 'center' }, + scroll: { paddingHorizontal: 20, paddingTop: 20, gap: 16 }, + + // Greeting + greetRow: { flexDirection: 'row', gap: 12, alignItems: 'center' }, + greetLeft: { flex: 1 }, + greetName: { fontSize: 26, fontWeight: '800', letterSpacing: -0.5, color: Colors.onSurface }, + greetDate: { fontSize: 14, color: Colors.onSurfaceVariant, marginTop: 2 }, + weatherCard: { + flexDirection: 'row', alignItems: 'center', gap: 12, + backgroundColor: Colors.surfaceContainerHigh, borderRadius: Radii.xl, + paddingHorizontal: 16, paddingVertical: 14, + }, + weatherTitle: { fontSize: 9, fontWeight: '800', letterSpacing: 1.5, color: Colors.onSurfaceVariant }, + weatherTemp: { fontSize: 26, fontWeight: '900', color: Colors.onSurface }, + weatherCond: { fontSize: 13, fontWeight: '600', color: Colors.onSurfaceVariant }, + + // Bento + bentoRow: { gap: 12 }, + cropCard: { + backgroundColor: Colors.surfaceContainerLowest, borderRadius: Radii.xxl, + padding: 20, gap: 14, + shadowColor: '#0b1c30', shadowOffset: { width: 0, height: 6 }, + shadowOpacity: 0.04, shadowRadius: 16, elevation: 3, + }, + cropCardTop: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'flex-start' }, + activeBadge: { + alignSelf: 'flex-start', backgroundColor: Colors.secondaryContainer, + borderRadius: Radii.full, paddingHorizontal: 10, paddingVertical: 4, marginBottom: 6, + }, + activeBadgeText: { fontSize: 11, fontWeight: '800', color: Colors.onSecondaryContainer, letterSpacing: 0.5 }, + cropName: { fontSize: 20, fontWeight: '700', color: Colors.onSurface }, + cropDay: { fontSize: 13, color: Colors.onSurfaceVariant, marginTop: 2 }, + progressTrack: { height: 10, backgroundColor: Colors.surfaceContainerHighest, borderRadius: 5, overflow: 'hidden' }, + progressFill: { height: '100%', borderRadius: 5 }, + progressLabels:{ flexDirection: 'row', justifyContent: 'space-between' }, + pLabel: { fontSize: 10, fontWeight: '700', color: Colors.onSurfaceVariant, textTransform: 'uppercase', letterSpacing: 0.3 }, + + soilCard: { + backgroundColor: Colors.surfaceContainerLow, borderRadius: Radii.xxl, + padding: 20, flexDirection: 'row', alignItems: 'center', gap: 16, + }, + moistureCircle: { + width: 76, height: 76, borderRadius: 38, + borderWidth: 8, borderColor: Colors.primary, + alignItems: 'center', justifyContent: 'center', backgroundColor: '#fff', + shadowColor: '#0b1c30', shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.08, shadowRadius: 8, elevation: 3, + }, + moistureValue: { fontSize: 18, fontWeight: '900', color: Colors.primary }, + soilInfo: { flex: 1, gap: 4 }, + soilTitle: { fontSize: 16, fontWeight: '800', color: Colors.onSurface }, + soilDesc: { fontSize: 13, color: Colors.onSurfaceVariant, lineHeight: 18 }, + soilIcons: { flexDirection: 'row', gap: 4, marginTop: 4 }, + + // Stats + statRow: { flexDirection: 'row', gap: 8 }, + statChip: { flex: 1, borderRadius: Radii.lg, paddingVertical: 10, paddingHorizontal: 12, alignItems: 'center', gap: 2 }, + statChipValue: { fontSize: 13, fontWeight: '800', color: Colors.onSurface }, + statChipLabel: { fontSize: 10, fontWeight: '600', color: Colors.onSurfaceVariant, textTransform: 'uppercase', letterSpacing: 0.5 }, + + // Crops CTA + cropsCta: { borderRadius: Radii.xl, overflow: 'hidden' }, + cropsCtaGrad: { + flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', + paddingHorizontal: 20, paddingVertical: 16, + }, + cropsCtaLeft: { flexDirection: 'row', alignItems: 'center', gap: 12 }, + cropsCtaTitle: { fontSize: 16, fontWeight: '800', color: Colors.onSurface }, + cropsCtaSub: { fontSize: 12, color: Colors.onSurfaceVariant }, + + // Aerial + aerialCard: { + height: 220, borderRadius: Radii.xxl, overflow: 'hidden', + shadowColor: '#0b1c30', shadowOffset: { width: 0, height: 12 }, + shadowOpacity: 0.12, shadowRadius: 24, elevation: 8, + }, + aerialImage: { width: '100%', height: '100%' }, + aerialLiveBadge: { + position: 'absolute', top: 16, left: 16, + flexDirection: 'row', alignItems: 'center', gap: 6, + backgroundColor: 'rgba(255,255,255,0.18)', borderRadius: Radii.full, + paddingHorizontal: 12, paddingVertical: 6, borderWidth: 1, borderColor: 'rgba(255,255,255,0.3)', + }, + liveDot: { width: 7, height: 7, borderRadius: 4, backgroundColor: '#ef4444' }, + liveBadgeText: { fontSize: 10, fontWeight: '900', letterSpacing: 1.5, color: '#fff' }, + aerialBottom: { position: 'absolute', bottom: 16, left: 16 }, + aerialTitle: { fontSize: 20, fontWeight: '900', color: '#fff', letterSpacing: -0.4 }, + aerialSub: { fontSize: 13, color: 'rgba(255,255,255,0.75)', marginTop: 2 }, + aerialFullBtn: { + position: 'absolute', bottom: 14, right: 14, + width: 44, height: 44, borderRadius: 22, + backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center', + shadowColor: '#000', shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.15, shadowRadius: 8, elevation: 4, + }, + + // Tasks + sectionTitle: { fontSize: 14, fontWeight: '900', letterSpacing: 1.2, color: Colors.onSurface, textTransform: 'uppercase', marginTop: 4 }, + taskCard: { + backgroundColor: Colors.surfaceContainerLowest, borderRadius: Radii.xxl, + padding: 20, flexDirection: 'row', alignItems: 'center', gap: 12, + shadowColor: '#0b1c30', shadowOffset: { width: 0, height: 6 }, + shadowOpacity: 0.05, shadowRadius: 16, elevation: 3, + }, + taskCardLeft: { flex: 1, gap: 6 }, + taskTag: { alignSelf: 'flex-start', borderRadius: 6, paddingHorizontal: 8, paddingVertical: 3 }, + taskTagText: { fontSize: 10, fontWeight: '900', color: Colors.onErrorContainer, letterSpacing: 0.5 }, + taskTitle: { fontSize: 16, fontWeight: '700', color: Colors.onSurface }, + taskDesc: { fontSize: 13, color: Colors.onSurfaceVariant, lineHeight: 18 }, + taskDue: { fontSize: 12, color: Colors.outline, fontStyle: 'italic' }, + taskDoneBtn: { + width: 44, height: 44, borderRadius: 14, + backgroundColor: Colors.primary, alignItems: 'center', justifyContent: 'center', + }, + taskMiniRow: { flexDirection: 'row', gap: 10 }, + taskMini: { + flex: 1, flexDirection: 'row', alignItems: 'center', gap: 12, + backgroundColor: Colors.surfaceContainerLow, borderRadius: Radii.xl, padding: 14, + }, + taskMiniIcon: { + width: 42, height: 42, borderRadius: 12, + backgroundColor: Colors.primaryFixed, alignItems: 'center', justifyContent: 'center', + }, + taskMiniSub: { fontSize: 11, color: Colors.outline, fontWeight: '700', textTransform: 'uppercase', letterSpacing: 0.4 }, + taskMiniTitle: { fontSize: 13, fontWeight: '700', color: Colors.onSurface, marginTop: 2 }, }); diff --git a/apps/mobile/app/(tabs)/farm/_layout.tsx b/apps/mobile/app/(tabs)/farm/_layout.tsx new file mode 100644 index 0000000..e48942c --- /dev/null +++ b/apps/mobile/app/(tabs)/farm/_layout.tsx @@ -0,0 +1,7 @@ +import { Stack } from 'expo-router'; + +// All farm sub-pages (map, fields, history, settings) are stacked +// on top of the My Farms index screen — they do NOT appear as tabs. +export default function FarmLayout() { + return ; +} diff --git a/apps/mobile/app/(tabs)/farm/fields.tsx b/apps/mobile/app/(tabs)/farm/fields.tsx new file mode 100644 index 0000000..591bd8b --- /dev/null +++ b/apps/mobile/app/(tabs)/farm/fields.tsx @@ -0,0 +1,79 @@ +import { View, Text, TouchableOpacity, StyleSheet, StatusBar, ScrollView, Image } from 'react-native'; +import { useRouter } from 'expo-router'; +import { MaterialIcons } from '@expo/vector-icons'; +import { Colors, Radii } from '@/constants/theme'; + +const FIELDS = [ + { id: '1', name: 'North Plot', size: '2.5 Acres', crop: 'Wheat', status: 'Active', img: 'https://images.unsplash.com/photo-1500382017468-9049fed747ef?w=400&q=70' }, + { id: '2', name: 'South Field', size: '1.8 Acres', crop: 'Corn', status: 'Fallow', img: 'https://images.unsplash.com/photo-1562516155-e0c1ee44059b?w=400&q=70' }, + { id: '3', name: 'East Block', size: '3.2 Acres', crop: 'Soybean', status: 'Planning', img: 'https://images.unsplash.com/photo-1416879595882-3373a0480b5b?w=400&q=70' }, +]; + +export default function MappedFieldsScreen() { + const router = useRouter(); + return ( + + + + router.back()} style={styles.backBtn}> + + + Mapped Fields + + + + {FIELDS.map(field => ( + + + + + {field.name} + + + {field.status} + + + + {field.size} • {field.crop} + + View Details + + + + + ))} + + + + ); +} + +const styles = StyleSheet.create({ + root: { flex: 1, backgroundColor: Colors.surface }, + header: { + flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', + paddingHorizontal: 20, paddingTop: 52, paddingBottom: 14, + backgroundColor: 'rgba(248,249,255,0.95)', + shadowColor: '#0b1c30', shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.06, shadowRadius: 12, elevation: 6, + }, + backBtn: { padding: 8, borderRadius: Radii.full, backgroundColor: Colors.surfaceContainerHigh }, + headerTitle: { fontSize: 18, fontWeight: '800', color: Colors.onSurface }, + scroll: { padding: 20, gap: 14 }, + fieldCard: { + backgroundColor: Colors.surfaceContainerLowest, borderRadius: Radii.xl, overflow: 'hidden', + shadowColor: '#0b1c30', shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.05, shadowRadius: 12, elevation: 3, + }, + fieldImg: { width: '100%', height: 130 }, + fieldInfo: { padding: 16, gap: 8 }, + fieldTop: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }, + fieldName: { fontSize: 17, fontWeight: '700', color: Colors.onSurface }, + statusBadge: { paddingHorizontal: 10, paddingVertical: 4, borderRadius: Radii.full, backgroundColor: Colors.surfaceContainerHigh }, + activeBadge: { backgroundColor: Colors.primaryFixed }, + statusText: { fontSize: 12, fontWeight: '700', color: Colors.onSurfaceVariant }, + activeText: { color: Colors.primary }, + fieldMeta: { fontSize: 13, color: Colors.onSurfaceVariant }, + detailBtn: { flexDirection: 'row', alignItems: 'center', gap: 4, alignSelf: 'flex-start', paddingTop: 4 }, + detailBtnText:{ fontSize: 13, fontWeight: '700', color: Colors.primary }, +}); diff --git a/apps/mobile/app/(tabs)/farm/history.tsx b/apps/mobile/app/(tabs)/farm/history.tsx new file mode 100644 index 0000000..5b78f15 --- /dev/null +++ b/apps/mobile/app/(tabs)/farm/history.tsx @@ -0,0 +1,68 @@ +import { View, Text, TouchableOpacity, StyleSheet, StatusBar, ScrollView } from 'react-native'; +import { useRouter } from 'expo-router'; +import { MaterialIcons } from '@expo/vector-icons'; +import { Colors, Radii } from '@/constants/theme'; + +const HISTORY = [ + { id: '1', date: 'Oct 24, 2024', action: 'Field boundary updated', field: 'North Plot', icon: 'edit-location-alt' as const }, + { id: '2', date: 'Oct 21, 2024', action: 'New field mapped', field: 'East Block', icon: 'add-location-alt' as const }, + { id: '3', date: 'Oct 18, 2024', action: 'Soil sample recorded', field: 'South Field', icon: 'science' as const }, + { id: '4', date: 'Oct 10, 2024', action: 'Satellite image synced', field: 'North Plot', icon: 'satellite-alt' as const }, + { id: '5', date: 'Oct 5, 2024', action: 'Irrigation mapped', field: 'East Block', icon: 'water' as const }, +]; + +export default function MappingHistoryScreen() { + const router = useRouter(); + return ( + + + + router.back()} style={styles.backBtn}> + + + Mapping History + + + + {HISTORY.map(item => ( + + + + + + {item.action} + {item.field} + + {item.date} + + ))} + + + + ); +} + +const styles = StyleSheet.create({ + root: { flex: 1, backgroundColor: Colors.surface }, + header: { + flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', + paddingHorizontal: 20, paddingTop: 52, paddingBottom: 14, + backgroundColor: 'rgba(248,249,255,0.95)', + shadowColor: '#0b1c30', shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.06, shadowRadius: 12, elevation: 6, + }, + backBtn: { padding: 8, borderRadius: Radii.full, backgroundColor: Colors.surfaceContainerHigh }, + headerTitle: { fontSize: 18, fontWeight: '800', color: Colors.onSurface }, + scroll: { padding: 20, gap: 12 }, + row: { + flexDirection: 'row', alignItems: 'center', gap: 14, + backgroundColor: Colors.surfaceContainerLowest, borderRadius: Radii.xl, padding: 16, + shadowColor: '#0b1c30', shadowOffset: { width: 0, height: 3 }, + shadowOpacity: 0.04, shadowRadius: 8, elevation: 2, + }, + iconBox: { width: 44, height: 44, borderRadius: 12, backgroundColor: Colors.primaryFixed, alignItems: 'center', justifyContent: 'center' }, + rowBody: { flex: 1 }, + rowAction: { fontSize: 14, fontWeight: '700', color: Colors.onSurface }, + rowField: { fontSize: 12, color: Colors.onSurfaceVariant, marginTop: 2 }, + rowDate: { fontSize: 11, color: Colors.outline, fontWeight: '600' }, +}); diff --git a/apps/mobile/app/(tabs)/farm/index.tsx b/apps/mobile/app/(tabs)/farm/index.tsx index ccd64ba..627efac 100644 --- a/apps/mobile/app/(tabs)/farm/index.tsx +++ b/apps/mobile/app/(tabs)/farm/index.tsx @@ -1,20 +1,282 @@ -import { View, Text, StyleSheet } from 'react-native'; +import { + View, Text, ScrollView, TouchableOpacity, + StyleSheet, StatusBar, Image, +} from 'react-native'; +import { useRouter } from 'expo-router'; +import { LinearGradient } from 'expo-linear-gradient'; +import { MaterialIcons } from '@expo/vector-icons'; +import { Colors, Radii } from '@/constants/theme'; + +const FARM_AERIAL = 'https://images.unsplash.com/photo-1500382017468-9049fed747ef?w=800&q=80'; +const HEATMAP_URL = 'https://images.unsplash.com/photo-1516912481808-3406841bd33c?w=600&q=80'; +const AVATAR_URL = 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=200&q=80'; + +type SoilPill = { label: string; value: string; bg: string; textColor: string }; +const SOIL_PILLS: SoilPill[] = [ + { label: 'N', value: 'Medium', bg: Colors.tertiaryFixed, textColor: Colors.onTertiaryContainer }, + { label: 'P', value: 'Low', bg: Colors.errorContainer, textColor: Colors.onErrorContainer }, + { label: 'K', value: 'High', bg: Colors.secondaryContainer, textColor: Colors.onSecondaryContainer }, +]; + +export default function MyFarmsScreen() { + const router = useRouter(); -/** - * Farm Management Screen - * TODO: Farm CRUD, field mapping, IoT device status - */ -export default function FarmScreen() { return ( - - My Farm - Farm management coming soon + + + + {/* Top Bar */} + + router.push('/profile' as any)} activeOpacity={0.85}> + + + Agronavis + + + + + + {/* Sub-nav: Map | Fields | History | Settings */} + + {[ + { label: 'Map', icon: 'map', route: '/(tabs)/farm/map' }, + { label: 'Fields', icon: 'layers', route: '/(tabs)/farm/fields' }, + { label: 'History', icon: 'history', route: '/(tabs)/farm/history' }, + { label: 'Settings', icon: 'settings', route: '/(tabs)/farm/settings' }, + ].map(({ label, icon, route }) => ( + router.push(route as any)} + activeOpacity={0.8} + > + + {label} + + ))} + + + + My Farms + Manage and monitor your active plots. + + {/* Main Farm Card */} + + + + + + Active Yield + + + 2.5 Acres + + + + + + + North Plot — 2.5 Acres + + + Sector 4G, Upper Basin + + + + Corn Hybrid Z4 + + + + {/* Soil Health Pills */} + Soil Health Profile + + {SOIL_PILLS.map(({ label, value, bg, textColor }) => ( + + {label} + {value} + + ))} + + + {/* Fertilizer Calculator */} + + + + + + Fertilizer Calculator + + + {[ + { icon: 'inventory-2', label: 'IFFCO Urea', qty: '3 Bags' }, + { icon: 'inventory', label: 'Single Super Phosphate', qty: '1 Bag' }, + ].map(({ label, qty }) => ( + + + + + + {qty} + {label} + + + ))} + + + + + + {/* Sidebar stats in a 2-col layout */} + + {/* Weather */} + + + + Today + + 28°C + Partly Cloudy • High Humidity + + + + Precipitation: 12% + + + {/* Heatmap */} + + + + + Live Heatmap + Updated 14 mins ago + + + + + + + + {/* Map New Farm FAB */} + router.push('/(tabs)/farm/map' as any)} + activeOpacity={0.88} + > + + + Map New Farm + + ); } const styles = StyleSheet.create({ - container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#0A2B15' }, - title: { fontSize: 28, fontWeight: '700', color: '#FFFFFF' }, - subtitle: { fontSize: 14, color: '#6B9F7E', marginTop: 8 }, + root: { flex: 1, backgroundColor: Colors.surface }, + topBar: { + flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', + paddingHorizontal: 20, paddingTop: 52, paddingBottom: 12, + backgroundColor: 'rgba(248,249,255,0.92)', + shadowColor: '#0b1c30', shadowOffset: { width: 0, height: 6 }, + shadowOpacity: 0.06, shadowRadius: 16, elevation: 8, + }, + avatar: { width: 40, height: 40, borderRadius: 20, borderWidth: 2, borderColor: Colors.primaryFixed }, + logo: { fontSize: 24, fontWeight: '900', letterSpacing: -0.8, color: Colors.primary }, + notifBtn: { width: 40, height: 40, alignItems: 'center', justifyContent: 'center' }, + + subNav: { + flexDirection: 'row', backgroundColor: Colors.surfaceContainerLow, + paddingVertical: 10, paddingHorizontal: 8, + borderBottomWidth: 1, borderBottomColor: Colors.outlineVariant, + }, + subNavBtn: { flex: 1, alignItems: 'center', gap: 3 }, + subNavLabel: { fontSize: 11, fontWeight: '700', color: Colors.primary, letterSpacing: 0.3 }, + + scroll: { paddingHorizontal: 20, paddingTop: 20, gap: 16 }, + pageTitle: { fontSize: 28, fontWeight: '800', letterSpacing: -0.5, color: Colors.onSurface }, + pageSub: { fontSize: 14, color: Colors.onSurfaceVariant }, + + farmCard: { + backgroundColor: Colors.surfaceContainerLowest, borderRadius: Radii.xxl, overflow: 'hidden', + shadowColor: '#0b1c30', shadowOffset: { width: 0, height: 8 }, + shadowOpacity: 0.06, shadowRadius: 24, elevation: 4, + }, + farmImgWrap: { height: 180, position: 'relative' }, + farmImg: { width: '100%', height: '100%' }, + farmImgBadges: { position: 'absolute', top: 14, left: 14, flexDirection: 'row', gap: 8 }, + imgBadge: { + backgroundColor: 'rgba(0,108,73,0.88)', borderRadius: Radii.full, + paddingHorizontal: 12, paddingVertical: 5, + }, + imgBadgeText: { fontSize: 11, fontWeight: '800', color: '#fff', letterSpacing: 0.5 }, + farmCardBody: { padding: 20, gap: 16 }, + farmCardHeader:{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'flex-start', flexWrap: 'wrap', gap: 8 }, + farmName: { fontSize: 18, fontWeight: '700', color: Colors.onSurface }, + farmLocation: { flexDirection: 'row', alignItems: 'center', gap: 2, marginTop: 2 }, + farmLocationText: { fontSize: 13, color: Colors.onSurfaceVariant }, + cropBadge: { + backgroundColor: Colors.surfaceContainerHigh, borderRadius: Radii.lg, + paddingHorizontal: 12, paddingVertical: 6, + }, + cropBadgeText: { fontSize: 13, fontWeight: '700', color: Colors.onSurface }, + + soilHeader: { fontSize: 11, fontWeight: '800', color: Colors.onSurfaceVariant, textTransform: 'uppercase', letterSpacing: 1.2 }, + soilPills: { flexDirection: 'row', gap: 8 }, + soilPill: { borderRadius: Radii.lg, paddingHorizontal: 14, paddingVertical: 8, flexDirection: 'row', alignItems: 'center', gap: 6 }, + soilPillLabel: { fontSize: 16, fontWeight: '900', color: Colors.onSurface }, + soilPillValue: { fontSize: 12, fontWeight: '700' }, + + fertCard: { + backgroundColor: Colors.surfaceContainer, borderRadius: Radii.xl, padding: 14, gap: 12, + }, + fertHeader: { flexDirection: 'row', alignItems: 'center', gap: 10 }, + fertIconWrap: { + width: 40, height: 40, borderRadius: 12, backgroundColor: Colors.primaryFixed, + alignItems: 'center', justifyContent: 'center', + }, + fertTitle: { fontSize: 15, fontWeight: '700', color: Colors.onSurface }, + fertItems: { flexDirection: 'row', gap: 10 }, + fertItem: { + flex: 1, flexDirection: 'row', alignItems: 'center', gap: 10, + backgroundColor: Colors.surfaceContainerLowest, borderRadius: Radii.lg, padding: 12, + }, + fertItemIcon: { + width: 52, height: 52, borderRadius: 12, backgroundColor: Colors.surfaceContainerHigh, + alignItems: 'center', justifyContent: 'center', + }, + fertQty: { fontSize: 20, fontWeight: '900', color: Colors.onSurface }, + fertLabel: { fontSize: 12, fontWeight: '700', color: Colors.primary }, + + sideRow: { flexDirection: 'row', gap: 12, height: 200 }, + weatherWidget: { + flex: 1, backgroundColor: Colors.surfaceContainerLow, borderRadius: Radii.xxl, padding: 16, gap: 4, + }, + weatherTop: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }, + weatherToday: { fontSize: 12, fontWeight: '700', color: Colors.onSurfaceVariant }, + weatherBigTemp:{ fontSize: 30, fontWeight: '900', color: Colors.onSurface }, + weatherCondition: { fontSize: 12, color: Colors.onSurfaceVariant }, + precipTrack: { height: 6, backgroundColor: '#fff', borderRadius: 3, overflow: 'hidden' }, + precipFill: { height: '100%', backgroundColor: Colors.tertiaryContainer, borderRadius: 3 }, + precipLabel: { fontSize: 10, fontWeight: '700', color: Colors.onSurfaceVariant, textTransform: 'uppercase', letterSpacing: 0.5 }, + + heatmapWrap: { flex: 1, borderRadius: Radii.xxl, overflow: 'hidden' }, + heatmapOverlay:{ ...StyleSheet.absoluteFillObject, backgroundColor: 'rgba(0,0,0,0.25)' }, + heatmapLabel: { + position: 'absolute', bottom: 0, left: 0, right: 0, + backgroundColor: 'rgba(255,255,255,0.82)', padding: 12, + }, + heatmapTitle: { fontSize: 14, fontWeight: '700', color: Colors.onSurface }, + heatmapSub: { fontSize: 11, color: Colors.onSurfaceVariant }, + + fab: { position: 'absolute', bottom: 98, right: 20 }, + fabGrad: { + flexDirection: 'row', alignItems: 'center', gap: 8, + paddingHorizontal: 18, paddingVertical: 14, borderRadius: Radii.xl, + shadowColor: Colors.primary, shadowOffset: { width: 0, height: 8 }, + shadowOpacity: 0.35, shadowRadius: 16, elevation: 8, + }, + fabLabel: { fontSize: 14, fontWeight: '700', color: '#fff' }, }); diff --git a/apps/mobile/app/(tabs)/farm/map.tsx b/apps/mobile/app/(tabs)/farm/map.tsx new file mode 100644 index 0000000..8abcdb9 --- /dev/null +++ b/apps/mobile/app/(tabs)/farm/map.tsx @@ -0,0 +1,89 @@ +import { View, Text, TouchableOpacity, StyleSheet, StatusBar, Image } from 'react-native'; +import { useRouter } from 'expo-router'; +import { LinearGradient } from 'expo-linear-gradient'; +import { MaterialIcons } from '@expo/vector-icons'; +import { Colors, Radii } from '@/constants/theme'; + +const MAP_URL = 'https://images.unsplash.com/photo-1560493676-04071c5f467b?w=800&q=80'; + +export default function MapNewFarmScreen() { + const router = useRouter(); + return ( + + + + router.back()} style={styles.backBtn}> + + + Map New Farm Area + + + + + Enter farm address or coordinates + + + + Use Current Location + + + + + + + + + Tap to drop pin + Long press to adjust boundary + + + + + + + Save Farm Area + + + + + ); +} + +const styles = StyleSheet.create({ + root: { flex: 1, backgroundColor: Colors.surface }, + header: { + flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', + paddingHorizontal: 20, paddingTop: 52, paddingBottom: 14, + backgroundColor: 'rgba(248,249,255,0.95)', + shadowColor: '#0b1c30', shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.06, shadowRadius: 12, elevation: 6, + }, + backBtn: { padding: 8, borderRadius: Radii.full, backgroundColor: Colors.surfaceContainerHigh }, + headerTitle: { fontSize: 18, fontWeight: '800', color: Colors.onSurface }, + searchBar: { + flexDirection: 'row', alignItems: 'center', gap: 10, margin: 16, + backgroundColor: Colors.surfaceContainerHighest, borderRadius: Radii.lg, height: 50, paddingHorizontal: 14, + }, + searchPlaceholder: { fontSize: 15, color: Colors.outline }, + locationBtn: { + flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 8, + backgroundColor: Colors.surfaceContainerHigh, borderRadius: Radii.lg, height: 46, marginHorizontal: 16, + }, + locationText: { fontSize: 15, fontWeight: '600', color: Colors.onSurface }, + mapWrap: { flex: 1, margin: 16, borderRadius: Radii.xl, overflow: 'hidden' }, + overlay: { ...StyleSheet.absoluteFillObject, backgroundColor: 'rgba(0,0,0,0.15)' }, + pinCenter: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, alignItems: 'center', justifyContent: 'center' }, + mapCard: { + position: 'absolute', bottom: 14, left: 14, right: 14, + backgroundColor: 'rgba(255,255,255,0.88)', borderRadius: Radii.lg, padding: 14, + alignItems: 'center', + }, + mapCardTitle: { fontSize: 15, fontWeight: '700', color: Colors.onSurface }, + mapCardSub: { fontSize: 12, color: Colors.onSurfaceVariant, marginTop: 2 }, + footer: { padding: 20, paddingBottom: 36 }, + saveBtn: { + flexDirection: 'row', alignItems: 'center', justifyContent: 'center', + gap: 8, height: 56, borderRadius: Radii.xxl, + }, + saveBtnText: { fontSize: 17, fontWeight: '700', color: '#fff' }, +}); diff --git a/apps/mobile/app/(tabs)/farm/settings.tsx b/apps/mobile/app/(tabs)/farm/settings.tsx new file mode 100644 index 0000000..1e4f10b --- /dev/null +++ b/apps/mobile/app/(tabs)/farm/settings.tsx @@ -0,0 +1,78 @@ +import { View, Text, TouchableOpacity, StyleSheet, StatusBar, Switch } from 'react-native'; +import { useRouter } from 'expo-router'; +import { MaterialIcons } from '@expo/vector-icons'; +import { Colors, Radii } from '@/constants/theme'; +import { useState } from 'react'; + +type SettingRow = { id: string; icon: React.ComponentProps['name']; title: string; sub: string; toggle?: boolean }; +const SETTINGS: SettingRow[] = [ + { id: 'auto_sync', icon: 'sync', title: 'Auto-Sync Satellite', sub: 'Update imagery every 6 hours', toggle: true }, + { id: 'gps', icon: 'gps-fixed', title: 'High-Accuracy GPS', sub: 'Use device GPS for mapping', toggle: true }, + { id: 'alerts', icon: 'notifications', title: 'Field Alerts', sub: 'Notify on boundary changes', toggle: false }, + { id: 'export', icon: 'file-download', title: 'Export Field Data', sub: 'Download CSV or GeoJSON', toggle: false }, + { id: 'units', icon: 'straighten', title: 'Measurement Units', sub: 'Acres / Hectares', toggle: false }, +]; + +export default function MappingSettingsScreen() { + const router = useRouter(); + const [toggles, setToggles] = useState>({ auto_sync: true, gps: true, alerts: false }); + return ( + + + + router.back()} style={styles.backBtn}> + + + Mapping Settings + + + + {SETTINGS.map(item => ( + + + + + + {item.title} + {item.sub} + + {item.toggle ? ( + setToggles(p => ({ ...p, [item.id]: !p[item.id] }))} + trackColor={{ false: Colors.surfaceContainerHigh, true: Colors.primaryFixed }} + thumbColor={toggles[item.id] ? Colors.primary : Colors.outline} + /> + ) : ( + + )} + + ))} + + + ); +} + +const styles = StyleSheet.create({ + root: { flex: 1, backgroundColor: Colors.surface }, + header: { + flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', + paddingHorizontal: 20, paddingTop: 52, paddingBottom: 14, + backgroundColor: 'rgba(248,249,255,0.95)', + shadowColor: '#0b1c30', shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.06, shadowRadius: 12, elevation: 6, + }, + backBtn: { padding: 8, borderRadius: Radii.full, backgroundColor: Colors.surfaceContainerHigh }, + headerTitle: { fontSize: 18, fontWeight: '800', color: Colors.onSurface }, + list: { padding: 20, gap: 10 }, + row: { + flexDirection: 'row', alignItems: 'center', gap: 14, + backgroundColor: Colors.surfaceContainerLowest, borderRadius: Radii.xl, padding: 16, + shadowColor: '#0b1c30', shadowOffset: { width: 0, height: 3 }, + shadowOpacity: 0.04, shadowRadius: 8, elevation: 2, + }, + iconBox: { width: 42, height: 42, borderRadius: 12, backgroundColor: Colors.primaryFixed, alignItems: 'center', justifyContent: 'center' }, + rowBody: { flex: 1 }, + rowTitle: { fontSize: 15, fontWeight: '700', color: Colors.onSurface }, + rowSub: { fontSize: 12, color: Colors.onSurfaceVariant, marginTop: 2 }, +}); diff --git a/apps/mobile/app/(tabs)/scan/_layout.tsx b/apps/mobile/app/(tabs)/scan/_layout.tsx new file mode 100644 index 0000000..39a9dec --- /dev/null +++ b/apps/mobile/app/(tabs)/scan/_layout.tsx @@ -0,0 +1,6 @@ +import { Stack } from 'expo-router'; + +// scan/result is stacked on top of the AI Scanner screen — does NOT appear as a tab. +export default function ScanLayout() { + return ; +} diff --git a/apps/mobile/app/(tabs)/scan/index.tsx b/apps/mobile/app/(tabs)/scan/index.tsx new file mode 100644 index 0000000..c9946ed --- /dev/null +++ b/apps/mobile/app/(tabs)/scan/index.tsx @@ -0,0 +1,203 @@ +import { View, Text, TouchableOpacity, StyleSheet, StatusBar, Image } from 'react-native'; +import { useRouter } from 'expo-router'; +import { MaterialIcons } from '@expo/vector-icons'; +import { Colors, Radii } from '@/constants/theme'; + +const SCAN_IMG = 'https://images.unsplash.com/photo-1530836369250-ef72a3f5cda8?w=600&q=80'; + +export default function ScanScreen() { + const router = useRouter(); + + return ( + + + + {/* Camera Area */} + + + + {/* Dark overlay */} + + + {/* Top bar inside camera */} + + Agronavis + + + + + + {/* Scanner frame corners */} + + + + + + + + {/* Hint */} + + Point camera at affected leaves + + + {/* Camera controls */} + + + + + + {/* Capture button */} + router.push('/(tabs)/scan/result' as any)} + activeOpacity={0.85} + > + + + + + + + + + + {/* Bottom section */} + + {/* Recent scan */} + Recent Scan Result + router.push('/(tabs)/scan/result' as any)} + activeOpacity={0.88} + > + + + DETECTED + + Early Blight + + Action Required + + + 94% confidence level + + + + + {/* Tips */} + + + + Avoid Midday Shadows + + + + Keep 15cm Distance + + + + + ); +} + +const CORNER_SIZE = 44; +const CORNER_THICK = 4; +const CORNER_COLOR = Colors.primaryContainer; + +const styles = StyleSheet.create({ + root: { flex: 1, backgroundColor: '#0d0d0d' }, + cameraWrap: { flex: 1.2, position: 'relative' }, + darkOverlay: { ...StyleSheet.absoluteFillObject, backgroundColor: 'rgba(0,0,0,0.35)' }, + camTopBar: { + position: 'absolute', top: 52, left: 20, right: 20, + flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', + }, + camLogo: { fontSize: 22, fontWeight: '900', color: '#fff', letterSpacing: -0.5 }, + camNotif: { width: 40, height: 40, alignItems: 'center', justifyContent: 'center' }, + + frameWrap: { + position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, + alignItems: 'center', justifyContent: 'center', + }, + cornerTL: { + position: 'absolute', top: '20%', left: '15%', + width: CORNER_SIZE, height: CORNER_SIZE, + borderTopWidth: CORNER_THICK, borderLeftWidth: CORNER_THICK, + borderColor: CORNER_COLOR, borderRadius: 6, + }, + cornerTR: { + position: 'absolute', top: '20%', right: '15%', + width: CORNER_SIZE, height: CORNER_SIZE, + borderTopWidth: CORNER_THICK, borderRightWidth: CORNER_THICK, + borderColor: CORNER_COLOR, borderRadius: 6, + }, + cornerBL: { + position: 'absolute', bottom: '28%', left: '15%', + width: CORNER_SIZE, height: CORNER_SIZE, + borderBottomWidth: CORNER_THICK, borderLeftWidth: CORNER_THICK, + borderColor: CORNER_COLOR, borderRadius: 6, + }, + cornerBR: { + position: 'absolute', bottom: '28%', right: '15%', + width: CORNER_SIZE, height: CORNER_SIZE, + borderBottomWidth: CORNER_THICK, borderRightWidth: CORNER_THICK, + borderColor: CORNER_COLOR, borderRadius: 6, + }, + hintWrap: { + position: 'absolute', bottom: '22%', left: 0, right: 0, alignItems: 'center', + }, + hintText: { + backgroundColor: 'rgba(0,0,0,0.4)', color: 'rgba(255,255,255,0.85)', + paddingHorizontal: 16, paddingVertical: 6, borderRadius: Radii.full, + fontSize: 13, fontWeight: '500', overflow: 'hidden', + }, + controls: { + position: 'absolute', bottom: 24, left: 0, right: 0, + flexDirection: 'row', alignItems: 'center', justifyContent: 'space-evenly', + }, + controlBtn: { + width: 48, height: 48, borderRadius: 24, + backgroundColor: 'rgba(255,255,255,0.15)', alignItems: 'center', justifyContent: 'center', + }, + captureBtn: { + width: 76, height: 76, borderRadius: 38, + backgroundColor: '#fff', padding: 5, + shadowColor: '#000', shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.3, shadowRadius: 8, elevation: 8, + }, + captureBtnInner: { + flex: 1, borderRadius: 33, + backgroundColor: Colors.primary, + }, + + bottomSection: { + backgroundColor: Colors.surface, paddingHorizontal: 20, paddingTop: 20, gap: 14, paddingBottom: 90, + }, + recentHeader: { fontSize: 16, fontWeight: '800', color: Colors.onSurface }, + recentCard: { + flexDirection: 'row', alignItems: 'center', gap: 14, + backgroundColor: Colors.surfaceContainerLowest, borderRadius: Radii.xl, padding: 14, + shadowColor: '#0b1c30', shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.05, shadowRadius: 12, elevation: 3, + }, + recentThumb: { width: 76, height: 76, borderRadius: Radii.lg }, + recentInfo: { flex: 1, gap: 4 }, + recentDetected: { fontSize: 10, fontWeight: '800', color: Colors.onSurfaceVariant, letterSpacing: 1 }, + recentTitleRow: { flexDirection: 'row', alignItems: 'center', gap: 8, flexWrap: 'wrap' }, + recentDisease: { fontSize: 17, fontWeight: '800', color: Colors.onSurface }, + actionBadge: { + backgroundColor: Colors.errorContainer, borderRadius: Radii.full, + paddingHorizontal: 8, paddingVertical: 3, + }, + actionBadgeText: { fontSize: 10, fontWeight: '800', color: Colors.onErrorContainer }, + recentConfidence:{ fontSize: 13, color: Colors.onSurfaceVariant }, + tipsRow: { flexDirection: 'row', gap: 12 }, + tipCard: { + flex: 1, backgroundColor: Colors.surfaceContainerLow, borderRadius: Radii.xl, + padding: 16, gap: 8, + }, + tipText: { fontSize: 13, fontWeight: '700', color: Colors.onSurface }, +}); diff --git a/apps/mobile/app/(tabs)/scan/result.tsx b/apps/mobile/app/(tabs)/scan/result.tsx new file mode 100644 index 0000000..dbc24e5 --- /dev/null +++ b/apps/mobile/app/(tabs)/scan/result.tsx @@ -0,0 +1,160 @@ +import { + View, Text, TouchableOpacity, StyleSheet, StatusBar, ScrollView, Image, +} from 'react-native'; +import { useRouter } from 'expo-router'; +import { LinearGradient } from 'expo-linear-gradient'; +import { MaterialIcons } from '@expo/vector-icons'; +import { Colors, Radii } from '@/constants/theme'; + +const LEAF_IMG = 'https://images.unsplash.com/photo-1518977676601-b53f82aba655?w=600&q=80'; + +const TREATMENT = [ + { step: 1, text: 'Remove and destroy infected plant debris immediately.' }, + { step: 2, text: 'Apply copper-based fungicide every 7–10 days.' }, + { step: 3, text: 'Avoid overhead irrigation to reduce moisture on foliage.' }, + { step: 4, text: 'Ensure good air circulation by proper plant spacing.' }, +]; + +export default function ScanResultScreen() { + const router = useRouter(); + return ( + + + + router.back()} style={styles.backBtn}> + + + Scan Result + + + + + + + {/* Scanned image */} + + + + + + + 94% Confidence + + + + + {/* Disease card */} + + + DETECTED + + Early Blight + Alternaria solani + + Severity + + + + Moderate + + + + {/* Treatment */} + + + + Treatment Plan + + {TREATMENT.map(({ step, text }) => ( + + + {step} + + {text} + + ))} + + + {/* Action button */} + router.replace('/(tabs)/scan' as any)}> + + + Scan Another Plant + + + + + + + ); +} + +const styles = StyleSheet.create({ + root: { flex: 1, backgroundColor: Colors.surface }, + header: { + flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', + paddingHorizontal: 20, paddingTop: 52, paddingBottom: 14, + backgroundColor: 'rgba(248,249,255,0.95)', + shadowColor: '#0b1c30', shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.06, shadowRadius: 12, elevation: 6, + }, + backBtn: { padding: 8, borderRadius: Radii.full, backgroundColor: Colors.surfaceContainerHigh }, + headerTitle: { fontSize: 18, fontWeight: '800', color: Colors.onSurface }, + shareBtn: { padding: 8, borderRadius: Radii.full, backgroundColor: Colors.surfaceContainerHigh }, + scroll: { padding: 20, gap: 16 }, + imageCard: { borderRadius: Radii.xxl, overflow: 'hidden', height: 220 }, + leafImage: { width: '100%', height: '100%' }, + imageOverlay: { ...StyleSheet.absoluteFillObject, backgroundColor: 'rgba(0,0,0,0.2)' }, + confidenceWrap: { position: 'absolute', bottom: 16, right: 16 }, + confidenceBadge:{ + flexDirection: 'row', alignItems: 'center', gap: 6, + backgroundColor: 'rgba(255,255,255,0.9)', borderRadius: Radii.full, + paddingHorizontal: 12, paddingVertical: 6, + }, + confidenceText: { fontSize: 13, fontWeight: '800', color: Colors.primary }, + + diseaseCard: { + backgroundColor: Colors.surfaceContainerLowest, borderRadius: Radii.xxl, padding: 20, gap: 8, + shadowColor: '#0b1c30', shadowOffset: { width: 0, height: 6 }, + shadowOpacity: 0.05, shadowRadius: 16, elevation: 3, + }, + diseaseBadge: { alignSelf: 'flex-start', backgroundColor: Colors.errorContainer, borderRadius: Radii.full, paddingHorizontal: 12, paddingVertical: 4 }, + diseaseBadgeText:{ fontSize: 11, fontWeight: '900', color: Colors.onErrorContainer, letterSpacing: 1 }, + diseaseName: { fontSize: 28, fontWeight: '900', letterSpacing: -0.5, color: Colors.onSurface }, + diseaseSci: { fontSize: 14, fontStyle: 'italic', color: Colors.onSurfaceVariant }, + severityRow: { flexDirection: 'row', alignItems: 'center', gap: 10, marginTop: 4 }, + severityLabel: { fontSize: 13, fontWeight: '600', color: Colors.onSurfaceVariant }, + severityTrack: { flex: 1, height: 8, backgroundColor: Colors.surfaceContainerHigh, borderRadius: 4, overflow: 'hidden' }, + severityFill: { height: '100%', borderRadius: 4 }, + severityValue: { fontSize: 13, fontWeight: '700', color: Colors.onSurface }, + + treatmentCard: { + backgroundColor: Colors.surfaceContainerLowest, borderRadius: Radii.xxl, padding: 20, gap: 14, + shadowColor: '#0b1c30', shadowOffset: { width: 0, height: 6 }, + shadowOpacity: 0.05, shadowRadius: 16, elevation: 3, + }, + treatmentHeader:{ flexDirection: 'row', alignItems: 'center', gap: 10 }, + treatmentTitle: { fontSize: 18, fontWeight: '800', color: Colors.onSurface }, + treatRow: { flexDirection: 'row', alignItems: 'flex-start', gap: 12 }, + stepBubble: { + width: 28, height: 28, borderRadius: 14, + backgroundColor: Colors.primaryFixed, alignItems: 'center', justifyContent: 'center', + marginTop: 2, + }, + stepNum: { fontSize: 13, fontWeight: '900', color: Colors.primary }, + stepText: { flex: 1, fontSize: 14, color: Colors.onSurface, lineHeight: 20 }, + newScanBtn: { + flexDirection: 'row', alignItems: 'center', justifyContent: 'center', + height: 56, borderRadius: Radii.xxl, gap: 10, + shadowColor: Colors.primary, shadowOffset: { width: 0, height: 8 }, + shadowOpacity: 0.3, shadowRadius: 16, elevation: 6, + }, + newScanText: { fontSize: 16, fontWeight: '700', color: '#fff' }, +}); diff --git a/apps/mobile/app/_layout.tsx b/apps/mobile/app/_layout.tsx index b0085bb..416658d 100644 --- a/apps/mobile/app/_layout.tsx +++ b/apps/mobile/app/_layout.tsx @@ -1,65 +1,19 @@ -import { ClerkProvider, useAuth } from '@clerk/clerk-expo'; -import { Slot, useRouter, useSegments } from 'expo-router'; -import { useEffect } from 'react'; +import { Stack } from 'expo-router'; import { GestureHandlerRootView } from 'react-native-gesture-handler'; -import { Provider } from 'react-redux'; -import { PersistGate } from 'redux-persist/integration/react'; -import { store, persistor } from '@store/index'; -import * as SecureStore from 'expo-secure-store'; +import { StatusBar } from 'react-native'; -// Clerk token cache — secure storage -const tokenCache = { - async getToken(key: string) { - try { - return SecureStore.getItemAsync(key); - } catch { - return null; - } - }, - async saveToken(key: string, value: string) { - try { - return SecureStore.setItemAsync(key, value); - } catch { - return; - } - }, -}; - -// Auth-aware route guard -function RouteGuard() { - const { isSignedIn, isLoaded } = useAuth(); - const segments = useSegments(); - const router = useRouter(); - - useEffect(() => { - if (!isLoaded) return; - - const inAuthGroup = segments[0] === '(auth)'; - - if (isSignedIn && inAuthGroup) { - router.replace('/(tabs)/dashboard'); - } else if (!isSignedIn && !inAuthGroup) { - router.replace('/(auth)/login'); - } - }, [isSignedIn, isLoaded, segments]); - - return ; -} - -// Root Layout export default function RootLayout() { return ( - - - - - - - + + + + + + {/* Standalone modal-style screens accessible from any tab */} + + + ); } diff --git a/apps/mobile/app/crops/index.tsx b/apps/mobile/app/crops/index.tsx new file mode 100644 index 0000000..e320d10 --- /dev/null +++ b/apps/mobile/app/crops/index.tsx @@ -0,0 +1,208 @@ +import { useState } from 'react'; +import { + View, Text, ScrollView, TouchableOpacity, + StyleSheet, StatusBar, TextInput, Image, +} from 'react-native'; +import { useRouter } from 'expo-router'; +import { LinearGradient } from 'expo-linear-gradient'; +import { MaterialIcons } from '@expo/vector-icons'; +import { Colors, Radii } from '@/constants/theme'; + +type Crop = { id: string; name: string; category: string; img: string }; + +const CATEGORIES = ['All', 'Grains', 'Legumes', 'Vegetables', 'Fruits', 'Fiber']; + +const CROPS: Crop[] = [ + { id: '1', name: 'Wheat', category: 'Grains', img: 'https://images.unsplash.com/photo-1574323347407-f5e1ad6d020b?w=400&q=70' }, + { id: '2', name: 'Soybean', category: 'Legumes', img: 'https://images.unsplash.com/photo-1595435742656-5272d0b3fa82?w=400&q=70' }, + { id: '3', name: 'Corn', category: 'Grains', img: 'https://images.unsplash.com/photo-1551754655-cd27e38d2076?w=400&q=70' }, + { id: '4', name: 'Cotton', category: 'Fiber', img: 'https://images.unsplash.com/photo-1595433707802-6b2626ef1c91?w=400&q=70' }, + { id: '5', name: 'Rice', category: 'Grains', img: 'https://images.unsplash.com/photo-1536304929831-ee1ca9d44906?w=400&q=70' }, + { id: '6', name: 'Tomato', category: 'Vegetables', img: 'https://images.unsplash.com/photo-1546094096-0df4bcaaa337?w=400&q=70' }, + { id: '7', name: 'Mango', category: 'Fruits', img: 'https://images.unsplash.com/photo-1553279768-865429fa0078?w=400&q=70' }, + { id: '8', name: 'Chickpea', category: 'Legumes', img: 'https://images.unsplash.com/photo-1585664811087-47f65abbad64?w=400&q=70' }, +]; + +export default function CropsScreen() { + const router = useRouter(); + const [search, setSearch] = useState(''); + const [category, setCategory] = useState('All'); + const [selected, setSelected] = useState>(new Set(['1', '4'])); + + const toggle = (id: string) => { + setSelected(prev => { + const next = new Set(prev); + next.has(id) ? next.delete(id) : next.add(id); + return next; + }); + }; + + const filtered = CROPS.filter(c => { + const matchCat = category === 'All' || c.category === category; + const matchSearch = c.name.toLowerCase().includes(search.toLowerCase()); + return matchCat && matchSearch; + }); + + return ( + + + + {/* Header */} + + router.back()} style={styles.backBtn}> + + + Select Your Crops + + + + + Choose the crops you're currently managing. + + + {/* Search */} + + + + + + {/* Categories */} + + {CATEGORIES.map(cat => ( + setCategory(cat)} + activeOpacity={0.8} + > + {cat} + + ))} + + + {/* Grid */} + + + {filtered.map(crop => { + const isSelected = selected.has(crop.id); + return ( + toggle(crop.id)} + activeOpacity={0.88} + > + {/* Selection badge */} + + + + + + {crop.name} + {crop.category} + + + ); + })} + + + + + {/* Confirm CTA */} + + router.back()} + activeOpacity={0.88} + > + 0 ? [Colors.primary, Colors.primaryContainer] : [Colors.outlineVariant, Colors.outlineVariant]} + style={styles.confirmBtn} + > + + Confirm Selection ({selected.size}) + + + + + + + ); +} + +const styles = StyleSheet.create({ + root: { flex: 1, backgroundColor: Colors.surface }, + header: { + flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', + paddingHorizontal: 20, paddingTop: 52, paddingBottom: 10, + backgroundColor: 'rgba(248,249,255,0.95)', + shadowColor: '#0b1c30', shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.06, shadowRadius: 12, elevation: 6, + }, + backBtn: { padding: 8, borderRadius: Radii.full, backgroundColor: Colors.surfaceContainerHigh }, + headerTitle: { fontSize: 18, fontWeight: '800', color: Colors.onSurface }, + headerSub: { fontSize: 14, color: Colors.onSurfaceVariant, paddingHorizontal: 20, marginTop: 10, marginBottom: 6 }, + searchWrap: { + flexDirection: 'row', alignItems: 'center', gap: 10, marginHorizontal: 20, + backgroundColor: Colors.surfaceContainerHighest, borderRadius: Radii.lg, height: 50, paddingHorizontal: 14, + }, + searchInput: { flex: 1, fontSize: 15, color: Colors.onSurface }, + catScroll: { marginTop: 12 }, + catContent: { paddingHorizontal: 20, gap: 8 }, + catChip: { + paddingHorizontal: 16, paddingVertical: 8, borderRadius: Radii.full, + backgroundColor: Colors.surfaceContainerHigh, + }, + catChipActive: { backgroundColor: Colors.primary }, + catText: { fontSize: 13, fontWeight: '600', color: Colors.onSurface }, + catTextActive: { color: '#fff' }, + grid: { paddingTop: 14, paddingBottom: 16 }, + gridInner: { flexDirection: 'row', flexWrap: 'wrap', paddingHorizontal: 16, gap: 12 }, + cropCard: { + width: '47%', backgroundColor: Colors.surfaceContainerLowest, + borderRadius: Radii.xl, overflow: 'hidden', + borderWidth: 2, borderColor: 'transparent', + shadowColor: '#0b1c30', shadowOffset: { width: 0, height: 3 }, + shadowOpacity: 0.04, shadowRadius: 8, elevation: 2, + }, + cropCardActive: { borderColor: Colors.primaryContainer }, + selBadge: { + position: 'absolute', top: 10, right: 10, zIndex: 2, + width: 26, height: 26, borderRadius: 13, + backgroundColor: 'rgba(255,255,255,0.6)', + alignItems: 'center', justifyContent: 'center', + borderWidth: 1, borderColor: Colors.outlineVariant, + }, + selBadgeActive: { backgroundColor: Colors.primaryContainer, borderColor: 'transparent' }, + cropImg: { width: '100%', height: 110 }, + cropInfo: { padding: 10 }, + cropName: { fontSize: 15, fontWeight: '700', color: Colors.onSurface }, + cropCategory: { fontSize: 11, color: Colors.onSurfaceVariant, marginTop: 2 }, + footer: { + position: 'absolute', bottom: 0, left: 0, right: 0, + paddingHorizontal: 20, paddingBottom: 36, paddingTop: 12, + backgroundColor: Colors.surface, + }, + confirmBtn: { + flexDirection: 'row', alignItems: 'center', justifyContent: 'center', + height: 56, borderRadius: Radii.xxl, gap: 10, + shadowColor: Colors.primary, shadowOffset: { width: 0, height: 8 }, + shadowOpacity: 0.28, shadowRadius: 16, elevation: 6, + }, + confirmText: { fontSize: 16, fontWeight: '700', color: '#fff' }, +}); diff --git a/apps/mobile/app/profile/index.tsx b/apps/mobile/app/profile/index.tsx new file mode 100644 index 0000000..4f1cd2a --- /dev/null +++ b/apps/mobile/app/profile/index.tsx @@ -0,0 +1,205 @@ +import { + View, Text, ScrollView, TouchableOpacity, + StyleSheet, StatusBar, Image, +} from 'react-native'; +import { useRouter } from 'expo-router'; +import { LinearGradient } from 'expo-linear-gradient'; +import { MaterialIcons } from '@expo/vector-icons'; +import { Colors, Radii } from '@/constants/theme'; + +const AVATAR_URL = 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=400&q=80'; + +const STATS = [ + { label: 'Total Acreage', value: '1,240', unit: 'ac', span: 2, icon: 'landscape' as const }, + { label: 'Active Crops', value: 'Wheat, Soy', unit: '', span: 1, icon: 'eco' as const }, + { label: 'Soil Health', value: '92%', unit: '', span: 1, icon: 'science' as const }, +]; + +const CERTS = [ + { icon: 'verified' as const, title: 'Organic Certified', sub: 'Valid until Dec 2025' }, + { icon: 'handshake' as const, title: 'Fair Trade', sub: 'Verified Partner' }, +]; + +export default function ProfileScreen() { + const router = useRouter(); + + return ( + + + + {/* Header */} + + router.back()} style={styles.backBtn}> + + + Profile + router.push('/(tabs)/farm/settings' as any)} + > + + + + + + {/* Profile hero */} + + + + + + + + Rajesh Kumar + + + Region: Northern Plains + + + + Edit Profile + + + + + {/* Farm Stats */} + Farm Stats + + {/* Wide card */} + + + + {STATS[0].label} + + {STATS[0].value} + {STATS[0].unit} + + + + + + {/* Two half cards */} + {STATS.slice(1).map(stat => ( + + {stat.label} + {stat.label === 'Soil Health' ? ( + <> + {stat.value} + + + + + ) : ( + {stat.value} + )} + + ))} + + + {/* Certifications */} + Certifications + + {CERTS.map(cert => ( + + + + + + {cert.title} + {cert.sub} + + + ))} + + + {/* Logout */} + router.replace('/(auth)/welcome')} + activeOpacity={0.85} + > + + Log Out + + + + + + ); +} + +const styles = StyleSheet.create({ + root: { flex: 1, backgroundColor: Colors.surface }, + header: { + flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', + paddingHorizontal: 20, paddingTop: 52, paddingBottom: 14, + backgroundColor: 'rgba(248,249,255,0.95)', + shadowColor: '#0b1c30', shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.06, shadowRadius: 12, elevation: 6, + }, + backBtn: { padding: 8, borderRadius: Radii.full, backgroundColor: Colors.surfaceContainerHigh }, + headerTitle: { fontSize: 18, fontWeight: '800', color: Colors.onSurface }, + settingsBtn: { padding: 8, borderRadius: Radii.full, backgroundColor: Colors.surfaceContainerHigh }, + scroll: { paddingHorizontal: 20, paddingTop: 24, gap: 14 }, + + hero: { alignItems: 'center', gap: 10 }, + avatarWrap: { position: 'relative' }, + avatar: { + width: 110, height: 110, borderRadius: 55, + borderWidth: 4, borderColor: Colors.surfaceContainerLowest, + shadowColor: '#0b1c30', shadowOffset: { width: 0, height: 10 }, + shadowOpacity: 0.1, shadowRadius: 20, elevation: 6, + }, + editAvatarBtn: { + position: 'absolute', bottom: 2, right: 2, width: 28, height: 28, + borderRadius: 14, backgroundColor: Colors.primaryContainer, + alignItems: 'center', justifyContent: 'center', + }, + name: { fontSize: 28, fontWeight: '900', letterSpacing: -0.6, color: Colors.onSurface }, + locationRow: { flexDirection: 'row', alignItems: 'center', gap: 4 }, + locationText: { fontSize: 14, color: Colors.onSurfaceVariant }, + editBtnWrap: { width: '100%', borderRadius: Radii.xl, overflow: 'hidden' }, + editBtn: { alignItems: 'center', justifyContent: 'center', height: 52 }, + editBtnText: { fontSize: 16, fontWeight: '700', color: Colors.onPrimary }, + + sectionTitle: { fontSize: 18, fontWeight: '800', color: Colors.onSurface, marginTop: 8 }, + + statsGrid: { flexDirection: 'row', flexWrap: 'wrap', gap: 10 }, + statCard: { + flex: 1, minWidth: '46%', backgroundColor: Colors.surfaceContainerLowest, + borderRadius: Radii.xl, padding: 18, gap: 6, + shadowColor: '#0b1c30', shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.04, shadowRadius: 12, elevation: 2, + }, + statCardWide: { minWidth: '100%', flexBasis: '100%' }, + statRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'flex-end' }, + statLabel: { fontSize: 12, color: Colors.onSurfaceVariant, fontWeight: '600' }, + statValue: { fontSize: 26, fontWeight: '900', letterSpacing: -0.5, color: Colors.onSurface }, + statUnit: { fontSize: 14, fontWeight: '400', color: Colors.onSurfaceVariant }, + healthTrack: { height: 6, backgroundColor: Colors.surfaceContainer, borderRadius: 3, overflow: 'hidden' }, + healthFill: { height: '100%', backgroundColor: Colors.primary, borderRadius: 3 }, + + certsList: { gap: 10 }, + certRow: { + flexDirection: 'row', alignItems: 'center', gap: 14, + backgroundColor: Colors.surfaceContainerLow, borderRadius: Radii.xl, padding: 16, + borderWidth: 1, borderColor: Colors.outlineVariant, + }, + certIcon: { + width: 46, height: 46, borderRadius: 14, + backgroundColor: Colors.secondaryContainer, + alignItems: 'center', justifyContent: 'center', + }, + certTitle: { fontSize: 15, fontWeight: '700', color: Colors.onSurface }, + certSub: { fontSize: 12, color: Colors.onSurfaceVariant, marginTop: 2 }, + + logoutBtn: { + flexDirection: 'row', alignItems: 'center', justifyContent: 'center', + gap: 8, paddingVertical: 16, borderRadius: Radii.xl, + backgroundColor: Colors.errorContainer, marginTop: 8, + }, + logoutText: { fontSize: 15, fontWeight: '700', color: Colors.error }, +}); diff --git a/apps/mobile/assets/images/adaptive-icon.png b/apps/mobile/assets/images/adaptive-icon.png new file mode 100644 index 0000000..e69de29 diff --git a/apps/mobile/assets/images/favicon.png b/apps/mobile/assets/images/favicon.png new file mode 100644 index 0000000..e69de29 diff --git a/apps/mobile/assets/images/icon.png b/apps/mobile/assets/images/icon.png new file mode 100644 index 0000000..e69de29 diff --git a/apps/mobile/assets/images/notification-icon.png b/apps/mobile/assets/images/notification-icon.png new file mode 100644 index 0000000..e69de29 diff --git a/apps/mobile/assets/images/splash-icon.png b/apps/mobile/assets/images/splash-icon.png new file mode 100644 index 0000000..e69de29 diff --git a/apps/mobile/babel.config.js b/apps/mobile/babel.config.js index 0382051..d6c32e0 100644 --- a/apps/mobile/babel.config.js +++ b/apps/mobile/babel.config.js @@ -24,7 +24,6 @@ module.exports = function (api) { extensions: ['.ios.js', '.android.js', '.js', '.jsx', '.ts', '.tsx', '.json'], }, ], - 'react-native-reanimated/plugin', ], }; }; diff --git a/apps/mobile/components/BottomNav.tsx b/apps/mobile/components/BottomNav.tsx new file mode 100644 index 0000000..f253056 --- /dev/null +++ b/apps/mobile/components/BottomNav.tsx @@ -0,0 +1,91 @@ +import React from 'react'; +import { View, Text, TouchableOpacity, StyleSheet } from 'react-native'; +import { useRouter, usePathname } from 'expo-router'; +import { LinearGradient } from 'expo-linear-gradient'; +import { MaterialIcons } from '@expo/vector-icons'; +import { Colors } from '@/constants/theme'; + +type TabItem = { + segment: string; + path: string; + label: string; + icon: React.ComponentProps['name']; +}; + +const TABS: TabItem[] = [ + { segment: 'dashboard', path: '/(tabs)/dashboard', label: 'Dashboard', icon: 'dashboard' }, + { segment: 'farm', path: '/(tabs)/farm', label: 'My Farms', icon: 'agriculture' }, + { segment: 'scan', path: '/(tabs)/scan', label: 'AI Scanner',icon: 'photo-camera' }, + { segment: 'community', path: '/(tabs)/community', label: 'Community', icon: 'groups' }, +]; + +export default function BottomNav() { + const router = useRouter(); + const pathname = usePathname(); + + const activeSegment = TABS.find(t => pathname.includes(`/${t.segment}`))?.segment ?? ''; + + return ( + + {TABS.map((tab) => { + const isActive = activeSegment === tab.segment; + return ( + router.push(tab.path as any)} + style={styles.btn} + activeOpacity={0.75} + > + {isActive ? ( + + + {tab.label} + + ) : ( + + + {tab.label} + + )} + + ); + })} + + ); +} + +const styles = StyleSheet.create({ + container: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-around', + paddingHorizontal: 8, + paddingTop: 12, + paddingBottom: 28, + backgroundColor: 'rgba(255,255,255,0.96)', + borderTopLeftRadius: 28, + borderTopRightRadius: 28, + shadowColor: '#0b1c30', + shadowOffset: { width: 0, height: -6 }, + shadowOpacity: 0.08, + shadowRadius: 20, + elevation: 20, + }, + btn: { flex: 1, alignItems: 'center' }, + pill: { + flexDirection: 'row', + alignItems: 'center', + gap: 5, + paddingHorizontal: 12, + paddingVertical: 8, + borderRadius: 16, + }, + pillLabel: { color: '#fff', fontSize: 11, fontWeight: '700', letterSpacing: 0.3 }, + inactiveItem: { alignItems: 'center', gap: 3 }, + inactiveLabel: { color: Colors.outline, fontSize: 10, fontWeight: '600', letterSpacing: 0.2, marginTop: 2 }, +}); diff --git a/apps/mobile/constants/theme.ts b/apps/mobile/constants/theme.ts new file mode 100644 index 0000000..81e8794 --- /dev/null +++ b/apps/mobile/constants/theme.ts @@ -0,0 +1,66 @@ +// ─── Agronavis Design Tokens ────────────────────────────────────────────────── +// Material You palette — single source of truth for the entire app. + +export const Colors = { + primary: '#006c49', + primaryContainer: '#10b981', + onPrimary: '#ffffff', + onPrimaryContainer: '#00422b', + primaryFixed: '#6ffbbe', + primaryFixedDim: '#4edea3', + inversePrimary: '#4edea3', + + secondary: '#1b6b51', + secondaryContainer: '#a6f2d1', + onSecondary: '#ffffff', + onSecondaryContainer: '#237157', + + tertiary: '#855300', + tertiaryContainer: '#e29100', + onTertiary: '#ffffff', + onTertiaryContainer: '#523200', + tertiaryFixed: '#ffddb8', + tertiaryFixedDim: '#ffb95f', + + error: '#ba1a1a', + errorContainer: '#ffdad6', + onError: '#ffffff', + onErrorContainer: '#93000a', + + surface: '#f8f9ff', + surfaceBright: '#f8f9ff', + surfaceDim: '#cbdbf5', + surfaceVariant: '#d3e4fe', + surfaceContainerLowest: '#ffffff', + surfaceContainerLow: '#eff4ff', + surfaceContainer: '#e5eeff', + surfaceContainerHigh: '#dce9ff', + surfaceContainerHighest: '#d3e4fe', + inverseSurface: '#213145', + inverseOnSurface: '#eaf1ff', + + onSurface: '#0b1c30', + onSurfaceVariant: '#3c4a42', + onBackground: '#0b1c30', + background: '#f8f9ff', + + outline: '#6c7a71', + outlineVariant: '#bbcabf', + surfaceTint: '#006c49', +} as const; + +export const Radii = { + sm: 8, + md: 12, + lg: 16, + xl: 24, + xxl: 32, + full: 9999, +} as const; + +export const FontFamily = { + black: 'PublicSans_900Black', + bold: 'PublicSans_700Bold', + semiBold: 'PublicSans_600SemiBold', + regular: 'PublicSans_400Regular', +} as const; diff --git a/apps/mobile/expo-env.d.ts b/apps/mobile/expo-env.d.ts index bf3c169..5411fdd 100644 --- a/apps/mobile/expo-env.d.ts +++ b/apps/mobile/expo-env.d.ts @@ -1,3 +1,3 @@ /// -// NOTE: This file should not be edited and should be in your git ignore +// NOTE: This file should not be edited and should be in your git ignore \ No newline at end of file diff --git a/apps/mobile/metro.config.js b/apps/mobile/metro.config.js index 78eaff4..194a763 100644 --- a/apps/mobile/metro.config.js +++ b/apps/mobile/metro.config.js @@ -16,5 +16,10 @@ config.resolver.resolveRequest = (context, moduleName, platform) => { } return context.resolveRequest(context, moduleName, platform); }; +// Ignore macOS metadata files +config.resolver.blockList = [ + ...Array.from(config.resolver.blockList || []), + /(^|\/)\._.*/ +]; module.exports = config; diff --git a/apps/mobile/mocks/react-native-maps.web.js b/apps/mobile/mocks/react-native-maps.web.js index 1ad2e2f..e0cf840 100644 --- a/apps/mobile/mocks/react-native-maps.web.js +++ b/apps/mobile/mocks/react-native-maps.web.js @@ -6,6 +6,9 @@ const MapView = ({ children, style }) => {children}; const Marker = () => null; const Polyline = () => null; const Polygon = () => null; + +export default MapView; +export { Marker, Polyline, Polygon }; const Circle = () => null; const Callout = ({ children }) => <>{children}; diff --git a/apps/mobile/package.json b/apps/mobile/package.json index bada383..3e7fe19 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -19,18 +19,19 @@ "test": "jest" }, "dependencies": { + "@agronavis/shared-types": "*", "@clerk/clerk-expo": "^2.15.2", "@expo/metro-runtime": "~6.1.2", "@expo/vector-icons": "^15.0.2", "@react-native-async-storage/async-storage": "^2.2.0", - "@react-native-community/datetimepicker": "^8.4.4", - "@react-native-picker/picker": "^2.11.2", + "@react-native-community/datetimepicker": "8.4.4", + "@react-native-picker/picker": "2.11.1", "@react-navigation/bottom-tabs": "^7.4.0", "@react-navigation/native": "^7.1.8", "@reduxjs/toolkit": "^2.9.0", - "@agronavis/shared-types": "*", "axios": "^1.12.2", "expo": "~54.0.10", + "expo-auth-session": "~7.0.11", "expo-camera": "^17.0.8", "expo-constants": "~18.0.9", "expo-file-system": "~19.0.21", @@ -43,6 +44,7 @@ "expo-location": "~19.0.8", "expo-notifications": "^0.32.11", "expo-router": "~6.0.8", + "expo-secure-store": "~15.0.8", "expo-sensors": "^15.0.7", "expo-splash-screen": "~31.0.10", "expo-status-bar": "~3.0.8", @@ -50,31 +52,34 @@ "lottie-react-native": "^7.3.4", "react": "19.1.0", "react-dom": "19.1.0", - "react-native": "0.81.4", + "react-native": "0.81.5", "react-native-chart-kit": "^6.12.0", "react-native-gesture-handler": "~2.28.0", "react-native-maps": "1.20.1", "react-native-modal": "^14.0.0-rc.1", "react-native-paper": "^5.14.5", - "react-native-reanimated": "~4.1.0", + "react-native-reanimated": "~4.1.1", "react-native-safe-area-context": "~5.6.0", "react-native-screens": "~4.16.0", - "react-native-svg": "^15.13.0", + "react-native-svg": "15.12.1", "react-native-web": "~0.21.0", + "react-native-worklets": "^0.8.3", "react-redux": "^9.2.0", "redux-persist": "^6.0.0", "zod": "^3.23.8" }, "devDependencies": { + "@expo/ngrok": "^4.1.3", + "@types/jest": "^29.5.12", "@types/react": "~19.1.0", "@types/react-native": "^0.72.8", - "@types/jest": "^29.5.12", "babel-plugin-module-resolver": "^5.0.2", "eslint": "^9.25.0", "eslint-config-expo": "~10.0.0", "jest": "^29.7.0", "jest-expo": "~54.0.0", "react-native-dotenv": "^3.4.11", + "react-native-svg-transformer": "^1.5.3", "typescript": "~5.9.2" }, "private": true, diff --git a/apps/mobile/services/cropService.ts b/apps/mobile/services/cropService.ts index 3dcedf9..518c857 100644 --- a/apps/mobile/services/cropService.ts +++ b/apps/mobile/services/cropService.ts @@ -1,6 +1,6 @@ import api from './api'; -import { ApiResponse } from '@types/api.types'; -import { Crop } from '@agronavis/shared-types'; +import { ApiResponse } from '../types/api.types'; +import { Crop } from '../../../packages/shared-types/src/index'; export const cropService = { getCrops: (farmId: string) => api.get>(`/crops?farmId=${farmId}`), diff --git a/apps/mobile/services/farmService.ts b/apps/mobile/services/farmService.ts index 17bdaea..c1cf71d 100644 --- a/apps/mobile/services/farmService.ts +++ b/apps/mobile/services/farmService.ts @@ -1,6 +1,6 @@ import api from './api'; -import { ApiResponse } from '@types/api.types'; -import { Farm } from '@agronavis/shared-types'; +import { ApiResponse } from '../types/api.types'; +import { Farm } from '../../../packages/shared-types/src/index'; export const farmService = { getMyFarms: () => api.get>('/farms'), diff --git a/apps/mobile/services/farmerService.ts b/apps/mobile/services/farmerService.ts index 82cd686..6487a4c 100644 --- a/apps/mobile/services/farmerService.ts +++ b/apps/mobile/services/farmerService.ts @@ -1,6 +1,6 @@ import api from './api'; -import { ApiResponse } from '@types/api.types'; -import { Farmer } from '@agronavis/shared-types'; +import { ApiResponse } from '../types/api.types'; +import { Farmer } from '../../../packages/shared-types/src/index'; export const farmerService = { /** Get current farmer's profile */ diff --git a/apps/mobile/services/weatherService.ts b/apps/mobile/services/weatherService.ts index dc730a1..c9cf6b2 100644 --- a/apps/mobile/services/weatherService.ts +++ b/apps/mobile/services/weatherService.ts @@ -1,6 +1,6 @@ import api from './api'; -import { ApiResponse } from '@types/api.types'; -import { WeatherData, Advisory } from '@agronavis/shared-types'; +import { ApiResponse } from '../types/api.types'; +import { WeatherData, Advisory } from '../../../packages/shared-types/src/index'; export const weatherService = { getCurrentWeather: (lat: number, lon: number) => diff --git a/apps/mobile/store/index.ts b/apps/mobile/store/index.ts index 4d00efe..1f38df4 100644 --- a/apps/mobile/store/index.ts +++ b/apps/mobile/store/index.ts @@ -22,11 +22,28 @@ const rootReducer = combineReducers({ ui: uiReducer, }); +import { Platform } from 'react-native'; + +const safeStorage = { + getItem: (key: string) => { + if (Platform.OS === 'web' && typeof window === 'undefined') return Promise.resolve(null); + return AsyncStorage.getItem(key); + }, + setItem: (key: string, value: string) => { + if (Platform.OS === 'web' && typeof window === 'undefined') return Promise.resolve(); + return AsyncStorage.setItem(key, value); + }, + removeItem: (key: string) => { + if (Platform.OS === 'web' && typeof window === 'undefined') return Promise.resolve(); + return AsyncStorage.removeItem(key); + }, +}; + // ─── Persist Config ─────────────────────────────────────────────────────────── const persistConfig = { key: 'agronavis-root', version: 1, - storage: AsyncStorage, + storage: safeStorage, whitelist: ['auth', 'farmer', 'ui'], // Only persist auth, farmer profile, and UI prefs }; diff --git a/apps/mobile/store/slices/farmerSlice.ts b/apps/mobile/store/slices/farmerSlice.ts index 1c2bc53..1092fd2 100644 --- a/apps/mobile/store/slices/farmerSlice.ts +++ b/apps/mobile/store/slices/farmerSlice.ts @@ -1,6 +1,6 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; -import { Farmer } from '@agronavis/shared-types'; -import { RequestStatus } from '@types/api.types'; +import { Farmer } from '../../../../packages/shared-types/src/index'; +import { RequestStatus } from '../../types/api.types'; interface FarmerState { profile: Farmer | null; diff --git a/apps/mobile/store/slices/uiSlice.ts b/apps/mobile/store/slices/uiSlice.ts index f1691d7..2a132c8 100644 --- a/apps/mobile/store/slices/uiSlice.ts +++ b/apps/mobile/store/slices/uiSlice.ts @@ -1,5 +1,5 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; -import { ThemeMode } from '@types/ui.types'; +import { ThemeMode } from '../../types/ui.types'; interface UIState { theme: ThemeMode; @@ -9,7 +9,7 @@ interface UIState { const uiSlice = createSlice({ name: 'ui', - initialState: { theme: 'dark' as ThemeMode, language: 'en', isLoading: false } satisfies UIState, + initialState: { theme: 'dark' as ThemeMode, language: 'en', isLoading: false as boolean } satisfies UIState, reducers: { setTheme: (state, action: PayloadAction) => { state.theme = action.payload; }, setLanguage: (state, action: PayloadAction) => { state.language = action.payload; }, diff --git a/apps/mobile/tsconfig.json b/apps/mobile/tsconfig.json index 9c538a9..0433afc 100644 --- a/apps/mobile/tsconfig.json +++ b/apps/mobile/tsconfig.json @@ -1,7 +1,10 @@ { "compilerOptions": { "target": "ESNext", - "lib": ["ESNext", "DOM"], + "lib": [ + "ESNext", + "DOM" + ], "allowJs": true, "jsx": "react-native", "noEmit": true, @@ -13,18 +16,42 @@ "allowSyntheticDefaultImports": true, "baseUrl": ".", "paths": { - "@/*": ["./*"], - "@app/*": ["./app/*"], - "@components/*": ["./components/*"], - "@features/*": ["./features/*"], - "@store/*": ["./store/*"], - "@services/*": ["./services/*"], - "@hooks/*": ["./hooks/*"], - "@contexts/*": ["./contexts/*"], - "@constants/*": ["./constants/*"], - "@utils/*": ["./utils/*"], - "@types/*": ["./types/*"], - "@assets/*": ["./assets/*"] + "@/*": [ + "./*" + ], + "@app/*": [ + "./app/*" + ], + "@components/*": [ + "./components/*" + ], + "@features/*": [ + "./features/*" + ], + "@store/*": [ + "./store/*" + ], + "@services/*": [ + "./services/*" + ], + "@hooks/*": [ + "./hooks/*" + ], + "@contexts/*": [ + "./contexts/*" + ], + "@constants/*": [ + "./constants/*" + ], + "@utils/*": [ + "./utils/*" + ], + "@types/*": [ + "./types/*" + ], + "@assets/*": [ + "./assets/*" + ] } }, "extends": "expo/tsconfig.base", @@ -32,7 +59,10 @@ "**/*.ts", "**/*.tsx", ".expo/types/**/*.d.ts", - "expo-env.d.ts" + "expo-env.d.ts", + ".expo/types/**/*.ts" ], - "exclude": ["node_modules"] + "exclude": [ + "node_modules" + ] } diff --git a/apps/mobile/types/index.ts b/apps/mobile/types/index.ts index 1e413b2..53e3d23 100644 --- a/apps/mobile/types/index.ts +++ b/apps/mobile/types/index.ts @@ -1,8 +1,5 @@ -// ─── Agronavis Shared Type Definitions ─────────────────────────────────────── -// Re-exports all shared types for use within the mobile app. -// Backend uses the same types from @agronavis/shared-types package. - -export * from '@agronavis/shared-types'; +// ─── Agronavis Mobile Type Definitions ─────────────────────────────────────── export * from './api.types'; export * from './navigation.types'; export * from './ui.types'; + diff --git a/backend/package.json b/backend/package.json index 5bd1ae8..8038d81 100644 --- a/backend/package.json +++ b/backend/package.json @@ -22,38 +22,39 @@ "@agronavis/shared-types": "*", "@clerk/express": "^1.0.0", "@prisma/client": "^5.15.0", + "bcryptjs": "^2.4.3", + "cors": "^2.8.5", + "dotenv": "^16.4.5", "express": "^4.21.0", "express-rate-limit": "^7.3.1", "helmet": "^8.0.0", - "morgan": "^1.10.0", - "cors": "^2.8.5", - "dotenv": "^16.4.5", - "zod": "^3.23.8", - "winston": "^3.14.1", - "bcryptjs": "^2.4.3", "jsonwebtoken": "^9.0.2", - "uuid": "^10.0.0", + "morgan": "^1.10.0", + "multer": "^1.4.5-lts.1", "node-cron": "^3.0.3", - "multer": "^1.4.5-lts.1" + "uuid": "^10.0.0", + "winston": "^3.14.1", + "zod": "^3.23.8" }, "devDependencies": { - "@types/express": "^4.17.21", - "@types/cors": "^2.8.17", - "@types/morgan": "^1.9.9", "@types/bcryptjs": "^2.4.6", + "@types/cors": "^2.8.17", + "@types/express": "^4.17.21", + "@types/jest": "^29.5.12", "@types/jsonwebtoken": "^9.0.6", + "@types/morgan": "^1.9.9", "@types/multer": "^1.4.11", "@types/node": "^20.14.11", "@types/node-cron": "^3.0.11", - "@types/jest": "^29.5.12", "@types/supertest": "^6.0.2", - "eslint": "^9.7.0", - "@typescript-eslint/parser": "^7.18.0", "@typescript-eslint/eslint-plugin": "^7.18.0", + "@typescript-eslint/parser": "^7.18.0", + "eslint": "^9.7.0", "jest": "^29.7.0", - "ts-jest": "^29.2.2", - "supertest": "^7.0.0", "prisma": "^5.15.0", + "supertest": "^7.0.0", + "ts-jest": "^29.2.2", + "ts-node": "^10.9.2", "tsx": "^4.16.2", "typescript": "^5.5.4" } diff --git a/backend/src/middleware/auth.middleware.ts b/backend/src/middleware/auth.middleware.ts index bb41b12..19fbea9 100644 --- a/backend/src/middleware/auth.middleware.ts +++ b/backend/src/middleware/auth.middleware.ts @@ -1,5 +1,5 @@ import { Request, Response, NextFunction } from 'express'; -import { clerkClient } from '@clerk/express'; +import { verifyToken } from '@clerk/express'; import { AppError } from './error.middleware'; /** Verifies Clerk JWT from Authorization header and attaches userId to request */ @@ -11,7 +11,8 @@ export async function authMiddleware(req: Request, _res: Response, next: NextFun } const token = authHeader.substring(7); - const { sub: userId } = await clerkClient.verifyToken(token); + const payload = await verifyToken(token, { secretKey: process.env.CLERK_SECRET_KEY as string }); + const userId = payload.sub; req.userId = userId; next(); diff --git a/backend/src/modules/farmers/farmers.service.ts b/backend/src/modules/farmers/farmers.service.ts index 04832e0..cef93ff 100644 --- a/backend/src/modules/farmers/farmers.service.ts +++ b/backend/src/modules/farmers/farmers.service.ts @@ -2,6 +2,8 @@ import prisma from '../../config/database'; import { CreateFarmerInput, UpdateFarmerInput } from './farmers.schema'; import { AppError } from '../../middleware/error.middleware'; +import { IrrigationType, SoilType } from '@prisma/client'; + export const farmerService = { async getByClerkId(clerkId: string) { return prisma.farmer.findUnique({ where: { clerkId } }); @@ -12,7 +14,12 @@ export const farmerService = { if (existing) throw new AppError(409, 'Farmer profile already exists'); return prisma.farmer.create({ - data: { ...data, clerkId }, + data: { + ...data, + clerkId, + irrigationType: data.irrigationType as IrrigationType | undefined, + soilType: data.soilType as SoilType | undefined, + }, }); }, @@ -20,7 +27,9 @@ export const farmerService = { const farmer = await prisma.farmer.findUnique({ where: { id } }); if (!farmer) throw new AppError(404, 'Farmer not found'); - return prisma.farmer.update({ where: { id }, data }); + const updateData: any = { ...data }; + + return prisma.farmer.update({ where: { id }, data: updateData }); }, async delete(id: string) { diff --git a/package-lock.json b/package-lock.json index c4d0396..e6d818e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,13 +29,14 @@ "@expo/metro-runtime": "~6.1.2", "@expo/vector-icons": "^15.0.2", "@react-native-async-storage/async-storage": "^2.2.0", - "@react-native-community/datetimepicker": "^8.4.4", - "@react-native-picker/picker": "^2.11.2", + "@react-native-community/datetimepicker": "8.4.4", + "@react-native-picker/picker": "2.11.1", "@react-navigation/bottom-tabs": "^7.4.0", "@react-navigation/native": "^7.1.8", "@reduxjs/toolkit": "^2.9.0", "axios": "^1.12.2", "expo": "~54.0.10", + "expo-auth-session": "~7.0.11", "expo-camera": "^17.0.8", "expo-constants": "~18.0.9", "expo-file-system": "~19.0.21", @@ -48,6 +49,7 @@ "expo-location": "~19.0.8", "expo-notifications": "^0.32.11", "expo-router": "~6.0.8", + "expo-secure-store": "~15.0.8", "expo-sensors": "^15.0.7", "expo-splash-screen": "~31.0.10", "expo-status-bar": "~3.0.8", @@ -55,22 +57,24 @@ "lottie-react-native": "^7.3.4", "react": "19.1.0", "react-dom": "19.1.0", - "react-native": "0.81.4", + "react-native": "0.81.5", "react-native-chart-kit": "^6.12.0", "react-native-gesture-handler": "~2.28.0", "react-native-maps": "1.20.1", "react-native-modal": "^14.0.0-rc.1", "react-native-paper": "^5.14.5", - "react-native-reanimated": "~4.1.0", + "react-native-reanimated": "~4.1.1", "react-native-safe-area-context": "~5.6.0", "react-native-screens": "~4.16.0", - "react-native-svg": "^15.13.0", + "react-native-svg": "15.12.1", "react-native-web": "~0.21.0", + "react-native-worklets": "^0.8.3", "react-redux": "^9.2.0", "redux-persist": "^6.0.0", "zod": "^3.23.8" }, "devDependencies": { + "@expo/ngrok": "^4.1.3", "@types/jest": "^29.5.12", "@types/react": "~19.1.0", "@types/react-native": "^0.72.8", @@ -80,9 +84,61 @@ "jest": "^29.7.0", "jest-expo": "~54.0.0", "react-native-dotenv": "^3.4.11", + "react-native-svg-transformer": "^1.5.3", "typescript": "~5.9.2" } }, + "apps/mobile/node_modules/@react-native-community/datetimepicker": { + "version": "8.4.4", + "resolved": "https://registry.npmjs.org/@react-native-community/datetimepicker/-/datetimepicker-8.4.4.tgz", + "integrity": "sha512-bc4ZixEHxZC9/qf5gbdYvIJiLZ5CLmEsC3j+Yhe1D1KC/3QhaIfGDVdUcid0PdlSoGOSEq4VlB93AWyetEyBSQ==", + "license": "MIT", + "dependencies": { + "invariant": "^2.2.4" + }, + "peerDependencies": { + "expo": ">=52.0.0", + "react": "*", + "react-native": "*", + "react-native-windows": "*" + }, + "peerDependenciesMeta": { + "expo": { + "optional": true + }, + "react-native-windows": { + "optional": true + } + } + }, + "apps/mobile/node_modules/@react-native-picker/picker": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@react-native-picker/picker/-/picker-2.11.1.tgz", + "integrity": "sha512-ThklnkK4fV3yynnIIRBkxxjxR4IFbdMNJVF6tlLdOJ/zEFUEFUEdXY0KmH0iYzMwY8W4/InWsLiA7AkpAbnexA==", + "license": "MIT", + "workspaces": [ + "example" + ], + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, + "apps/mobile/node_modules/react-native-svg": { + "version": "15.12.1", + "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.12.1.tgz", + "integrity": "sha512-vCuZJDf8a5aNC2dlMovEv4Z0jjEUET53lm/iILFnFewa15b4atjVxU6Wirm6O9y6dEsdjDZVD7Q3QM4T1wlI8g==", + "license": "MIT", + "dependencies": { + "css-select": "^5.1.0", + "css-tree": "^1.1.3", + "warn-once": "0.1.1" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, "backend": { "name": "@agronavis/backend", "version": "1.0.0", @@ -122,6 +178,7 @@ "prisma": "^5.15.0", "supertest": "^7.0.0", "ts-jest": "^29.2.2", + "ts-node": "^10.9.2", "tsx": "^4.16.2", "typescript": "^5.5.4" } @@ -1524,6 +1581,21 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", + "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-transform-typescript": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.6.tgz", @@ -1930,6 +2002,30 @@ "node": ">=0.1.90" } }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@dabh/diagnostics": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz", @@ -3529,6 +3625,198 @@ } } }, + "node_modules/@expo/ngrok": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@expo/ngrok/-/ngrok-4.1.3.tgz", + "integrity": "sha512-AESYaROGIGKWwWmUyQoUXcbvaUZjmpecC5buArXxYou+RID813F8T0Y5jQ2HUY49mZpYfJiy9oh4VSN37GgrXA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@expo/ngrok-bin": "2.3.42", + "got": "^11.5.1", + "uuid": "^3.3.2", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/@expo/ngrok-bin": { + "version": "2.3.42", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin/-/ngrok-bin-2.3.42.tgz", + "integrity": "sha512-kyhORGwv9XpbPeNIrX6QZ9wDVCDOScyTwxeS+ScNmUqYoZqD9LRmEqF7bpDh5VonTsrXgWrGl7wD2++oSHcaTQ==", + "dev": true, + "bin": { + "ngrok": "bin/ngrok.js" + }, + "optionalDependencies": { + "@expo/ngrok-bin-darwin-arm64": "2.3.41", + "@expo/ngrok-bin-darwin-x64": "2.3.41", + "@expo/ngrok-bin-freebsd-ia32": "2.3.41", + "@expo/ngrok-bin-freebsd-x64": "2.3.41", + "@expo/ngrok-bin-linux-arm": "2.3.41", + "@expo/ngrok-bin-linux-arm64": "2.3.41", + "@expo/ngrok-bin-linux-ia32": "2.3.41", + "@expo/ngrok-bin-linux-x64": "2.3.41", + "@expo/ngrok-bin-sunos-x64": "2.3.41", + "@expo/ngrok-bin-win32-ia32": "2.3.41", + "@expo/ngrok-bin-win32-x64": "2.3.41" + } + }, + "node_modules/@expo/ngrok-bin-darwin-arm64": { + "version": "2.3.41", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-darwin-arm64/-/ngrok-bin-darwin-arm64-2.3.41.tgz", + "integrity": "sha512-TPf95xp6SkvbRONZjltTOFcCJbmzAH7lrQ36Dv+djrOckWGPVq4HCur48YAeiGDqspmFEmqZ7ykD5c/bDfRFOA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@expo/ngrok-bin-darwin-x64": { + "version": "2.3.41", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-darwin-x64/-/ngrok-bin-darwin-x64-2.3.41.tgz", + "integrity": "sha512-29QZHfX4Ec0p0pQF5UrqiP2/Qe7t2rI96o+5b8045VCEl9AEAKHceGuyo+jfUDR4FSQBGFLSDb06xy8ghL3ZYA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@expo/ngrok-bin-freebsd-ia32": { + "version": "2.3.41", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-freebsd-ia32/-/ngrok-bin-freebsd-ia32-2.3.41.tgz", + "integrity": "sha512-YYXgwNZ+p0aIrwgb+1/RxJbsWhGEzBDBhZulKg1VB7tKDAd2C8uGnbK1rOCuZy013iOUsJDXaj9U5QKc13iIXw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@expo/ngrok-bin-freebsd-x64": { + "version": "2.3.41", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-freebsd-x64/-/ngrok-bin-freebsd-x64-2.3.41.tgz", + "integrity": "sha512-1Ei6K8BB+3etmmBT0tXYC4dyVkJMigT4ELbRTF5jKfw1pblqeXM9Qpf3p8851PTlH142S3bockCeO39rSkOnkg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@expo/ngrok-bin-linux-arm": { + "version": "2.3.41", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-linux-arm/-/ngrok-bin-linux-arm-2.3.41.tgz", + "integrity": "sha512-B6+rW/+tEi7ZrKWQGkRzlwmKo7c1WJhNODFBSgkF/Sj9PmmNhBz67mer91S2+6nNt5pfcwLLd61CjtWfR1LUHQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@expo/ngrok-bin-linux-arm64": { + "version": "2.3.41", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-linux-arm64/-/ngrok-bin-linux-arm64-2.3.41.tgz", + "integrity": "sha512-eC8GA/xPcmQJy4h+g2FlkuQB3lf5DjITy8Y6GyydmPYMByjUYAGEXe0brOcP893aalAzRqbNOAjSuAw1lcCLSQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@expo/ngrok-bin-linux-ia32": { + "version": "2.3.41", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-linux-ia32/-/ngrok-bin-linux-ia32-2.3.41.tgz", + "integrity": "sha512-w5Cy31wSz4jYnygEHS7eRizR1yt8s9TX6kHlkjzayIiRTFRb2E1qD2l0/4T2w0LJpBjM5ZFPaaKqsNWgCUIEow==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@expo/ngrok-bin-linux-x64": { + "version": "2.3.41", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-linux-x64/-/ngrok-bin-linux-x64-2.3.41.tgz", + "integrity": "sha512-LcU3MbYHv7Sn2eFz8Yzo2rXduufOvX1/hILSirwCkH+9G8PYzpwp2TeGqVWuO+EmvtBe6NEYwgdQjJjN6I4L1A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@expo/ngrok-bin-sunos-x64": { + "version": "2.3.41", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-sunos-x64/-/ngrok-bin-sunos-x64-2.3.41.tgz", + "integrity": "sha512-bcOj45BLhiV2PayNmLmEVZlFMhEiiGpOr36BXC0XSL+cHUZHd6uNaS28AaZdz95lrRzGpeb0hAF8cuJjo6nq4g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ] + }, + "node_modules/@expo/ngrok-bin-win32-ia32": { + "version": "2.3.41", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-win32-ia32/-/ngrok-bin-win32-ia32-2.3.41.tgz", + "integrity": "sha512-0+vPbKvUA+a9ERgiAknmZCiWA3AnM5c6beI+51LqmjKEM4iAAlDmfXNJ89aAbvZMUtBNwEPHzJHnaM4s2SeBhA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@expo/ngrok-bin-win32-x64": { + "version": "2.3.41", + "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-win32-x64/-/ngrok-bin-win32-x64-2.3.41.tgz", + "integrity": "sha512-mncsPRaG462LiYrM8mQT8OYe3/i44m3N/NzUeieYpGi8+pCOo8TIC23kR9P93CVkbM9mmXsy3X6hq91a8FWBdA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@expo/ngrok/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "bin/uuid" + } + }, "node_modules/@expo/osascript": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/@expo/osascript/-/osascript-2.4.3.tgz", @@ -4477,14 +4765,14 @@ "version": "5.22.0", "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.22.0.tgz", "integrity": "sha512-AUt44v3YJeggO2ZU5BkXI7M4hu9BF2zzH2iF2V5pyXT/lRTyWiElZ7It+bRH1EshoMRxHgpYg4VB6rCM+mG5jQ==", - "dev": true, + "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/engines": { "version": "5.22.0", "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.22.0.tgz", "integrity": "sha512-UNjfslWhAt06kVL3CjkuYpHAWSO6L4kDCVPegV6itt7nD1kSJavd3vhgAEhjglLJJKEdJ7oIqDJ+yHk6qO8gPA==", - "dev": true, + "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -4498,14 +4786,14 @@ "version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2.tgz", "integrity": "sha512-2PTmxFR2yHW/eB3uqWtcgRcgAbG1rwG9ZriSvQw+nnb7c4uCr3RAcGMb6/zfE88SKlC1Nj2ziUvc96Z379mHgQ==", - "dev": true, + "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/fetch-engine": { "version": "5.22.0", "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.22.0.tgz", "integrity": "sha512-bkrD/Mc2fSvkQBV5EpoFcZ87AvOgDxbG99488a5cexp5Ccny+UM6MAe/UFkUC0wLYD9+9befNOqGiIJhhq+HbA==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@prisma/debug": "5.22.0", @@ -4517,7 +4805,7 @@ "version": "5.22.0", "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.22.0.tgz", "integrity": "sha512-pHhpQdr1UPFpt+zFfnPazhulaZYCUqeIcPpJViYoq9R+D/yw4fjE+CtnsnKzPYm0ddUbeXUzjGVGIRVgPDCk4Q==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@prisma/debug": "5.22.0" @@ -5022,46 +5310,10 @@ "react-native": "^0.0.0-0 || >=0.65 <1.0" } }, - "node_modules/@react-native-community/datetimepicker": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/@react-native-community/datetimepicker/-/datetimepicker-8.6.0.tgz", - "integrity": "sha512-yxPSqNfxgpGaqHQIpatqe6ykeBdU/1pdsk/G3x01mY2bpTflLpmVTLqFSJYd3MiZzxNZcMs/j1dQakUczSjcYA==", - "license": "MIT", - "dependencies": { - "invariant": "^2.2.4" - }, - "peerDependencies": { - "expo": ">=52.0.0", - "react": "*", - "react-native": "*", - "react-native-windows": "*" - }, - "peerDependenciesMeta": { - "expo": { - "optional": true - }, - "react-native-windows": { - "optional": true - } - } - }, - "node_modules/@react-native-picker/picker": { - "version": "2.11.4", - "resolved": "https://registry.npmjs.org/@react-native-picker/picker/-/picker-2.11.4.tgz", - "integrity": "sha512-Kf8h1AMnBo54b1fdiVylP2P/iFcZqzpMYcglC28EEFB1DEnOjsNr6Ucqc+3R9e91vHxEDnhZFbYDmAe79P2gjA==", - "license": "MIT", - "workspaces": [ - "example" - ], - "peerDependencies": { - "react": "*", - "react-native": "*" - } - }, "node_modules/@react-native/assets-registry": { - "version": "0.81.4", - "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.81.4.tgz", - "integrity": "sha512-AMcDadefBIjD10BRqkWw+W/VdvXEomR6aEZ0fhQRAv7igrBzb4PTn4vHKYg+sUK0e3wa74kcMy2DLc/HtnGcMA==", + "version": "0.81.5", + "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.81.5.tgz", + "integrity": "sha512-705B6x/5Kxm1RKRvSv0ADYWm5JOnoiQ1ufW7h8uu2E6G9Of/eE6hP/Ivw3U5jI16ERqZxiKQwk34VJbB0niX9w==", "license": "MIT", "engines": { "node": ">= 20.19.4" @@ -5204,12 +5456,12 @@ } }, "node_modules/@react-native/community-cli-plugin": { - "version": "0.81.4", - "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.81.4.tgz", - "integrity": "sha512-8mpnvfcLcnVh+t1ok6V9eozWo8Ut+TZhz8ylJ6gF9d6q9EGDQX6s8jenan5Yv/pzN4vQEKI4ib2pTf/FELw+SA==", + "version": "0.81.5", + "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.81.5.tgz", + "integrity": "sha512-yWRlmEOtcyvSZ4+OvqPabt+NS36vg0K/WADTQLhrYrm9qdZSuXmq8PmdJWz/68wAqKQ+4KTILiq2kjRQwnyhQw==", "license": "MIT", "dependencies": { - "@react-native/dev-middleware": "0.81.4", + "@react-native/dev-middleware": "0.81.5", "debug": "^4.4.0", "invariant": "^2.2.4", "metro": "^0.83.1", @@ -5233,46 +5485,6 @@ } } }, - "node_modules/@react-native/community-cli-plugin/node_modules/@react-native/debugger-frontend": { - "version": "0.81.4", - "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.81.4.tgz", - "integrity": "sha512-SU05w1wD0nKdQFcuNC9D6De0ITnINCi8MEnx9RsTD2e4wN83ukoC7FpXaPCYyP6+VjFt5tUKDPgP1O7iaNXCqg==", - "license": "BSD-3-Clause", - "engines": { - "node": ">= 20.19.4" - } - }, - "node_modules/@react-native/community-cli-plugin/node_modules/@react-native/dev-middleware": { - "version": "0.81.4", - "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.81.4.tgz", - "integrity": "sha512-hu1Wu5R28FT7nHXs2wWXvQ++7W7zq5GPY83llajgPlYKznyPLAY/7bArc5rAzNB7b0kwnlaoPQKlvD/VP9LZug==", - "license": "MIT", - "dependencies": { - "@isaacs/ttlcache": "^1.4.1", - "@react-native/debugger-frontend": "0.81.4", - "chrome-launcher": "^0.15.2", - "chromium-edge-launcher": "^0.2.0", - "connect": "^3.6.5", - "debug": "^4.4.0", - "invariant": "^2.2.4", - "nullthrows": "^1.1.1", - "open": "^7.0.3", - "serve-static": "^1.16.2", - "ws": "^6.2.3" - }, - "engines": { - "node": ">= 20.19.4" - } - }, - "node_modules/@react-native/community-cli-plugin/node_modules/ws": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", - "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", - "license": "MIT", - "dependencies": { - "async-limiter": "~1.0.0" - } - }, "node_modules/@react-native/debugger-frontend": { "version": "0.81.5", "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.81.5.tgz", @@ -5314,28 +5526,606 @@ } }, "node_modules/@react-native/gradle-plugin": { - "version": "0.81.4", - "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.81.4.tgz", - "integrity": "sha512-T7fPcQvDDCSusZFVSg6H1oVDKb/NnVYLnsqkcHsAF2C2KGXyo3J7slH/tJAwNfj/7EOA2OgcWxfC1frgn9TQvw==", + "version": "0.81.5", + "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.81.5.tgz", + "integrity": "sha512-hORRlNBj+ReNMLo9jme3yQ6JQf4GZpVEBLxmTXGGlIL78MAezDZr5/uq9dwElSbcGmLEgeiax6e174Fie6qPLg==", "license": "MIT", "engines": { "node": ">= 20.19.4" } }, "node_modules/@react-native/js-polyfills": { - "version": "0.81.4", - "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.81.4.tgz", - "integrity": "sha512-sr42FaypKXJHMVHhgSbu2f/ZJfrLzgaoQ+HdpRvKEiEh2mhFf6XzZwecyLBvWqf2pMPZa+CpPfNPiejXjKEy8w==", + "version": "0.81.5", + "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.81.5.tgz", + "integrity": "sha512-fB7M1CMOCIUudTRuj7kzxIBTVw2KXnsgbQ6+4cbqSxo8NmRRhA0Ul4ZUzZj3rFd3VznTL4Brmocv1oiN0bWZ8w==", "license": "MIT", "engines": { "node": ">= 20.19.4" } }, - "node_modules/@react-native/normalize-colors": { - "version": "0.81.5", - "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.81.5.tgz", - "integrity": "sha512-0HuJ8YtqlTVRXGZuGeBejLE04wSQsibpTI+RGOyVqxZvgtlLLC/Ssw0UmbHhT4lYMp2fhdtvKZSs5emWB1zR/g==", - "license": "MIT" + "node_modules/@react-native/metro-babel-transformer": { + "version": "0.85.3", + "resolved": "https://registry.npmjs.org/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.85.3.tgz", + "integrity": "sha512-omuKq+r7jM4XvCMIlNMPP7Up3SyB8o5EAdZtF7YXniKyq7UOMBqhYHFqgsdOXr0lT+3ADf7VCJG3sb82jlBrrQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/core": "^7.25.2", + "@react-native/babel-preset": "0.85.3", + "hermes-parser": "0.33.3", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + }, + "peerDependencies": { + "@babel/core": "*" + } + }, + "node_modules/@react-native/metro-babel-transformer/node_modules/@react-native/babel-plugin-codegen": { + "version": "0.85.3", + "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.85.3.tgz", + "integrity": "sha512-Wc94zGfeFG8Njf9SHMPfYZP04kjigkOps6F1TYTvd7ZVXuGxqseCDgxc50LWcOhOCLypI9n3oVVqz81C3p44ZA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/traverse": "^7.29.0", + "@react-native/codegen": "0.85.3" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/@react-native/metro-babel-transformer/node_modules/@react-native/babel-preset": { + "version": "0.85.3", + "resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.85.3.tgz", + "integrity": "sha512-fD7fxEhkJB/aF57tWoXjaAWpklfrExYZS3k6aXPP3BQ77DZY7gvf/b7dbirwjID6NVnP1JDRJyTuPBGr0K/vlw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/plugin-proposal-export-default-from": "^7.24.7", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-default-from": "^7.24.7", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-transform-async-generator-functions": "^7.25.4", + "@babel/plugin-transform-async-to-generator": "^7.24.7", + "@babel/plugin-transform-block-scoping": "^7.25.0", + "@babel/plugin-transform-class-properties": "^7.25.4", + "@babel/plugin-transform-classes": "^7.25.4", + "@babel/plugin-transform-destructuring": "^7.24.8", + "@babel/plugin-transform-flow-strip-types": "^7.25.2", + "@babel/plugin-transform-for-of": "^7.24.7", + "@babel/plugin-transform-modules-commonjs": "^7.24.8", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", + "@babel/plugin-transform-optional-catch-binding": "^7.24.7", + "@babel/plugin-transform-optional-chaining": "^7.24.8", + "@babel/plugin-transform-private-methods": "^7.24.7", + "@babel/plugin-transform-private-property-in-object": "^7.24.7", + "@babel/plugin-transform-react-display-name": "^7.24.7", + "@babel/plugin-transform-react-jsx": "^7.25.2", + "@babel/plugin-transform-react-jsx-self": "^7.24.7", + "@babel/plugin-transform-react-jsx-source": "^7.24.7", + "@babel/plugin-transform-regenerator": "^7.24.7", + "@babel/plugin-transform-runtime": "^7.24.7", + "@babel/plugin-transform-typescript": "^7.25.2", + "@babel/plugin-transform-unicode-regex": "^7.24.7", + "@react-native/babel-plugin-codegen": "0.85.3", + "babel-plugin-syntax-hermes-parser": "0.33.3", + "babel-plugin-transform-flow-enums": "^0.0.2", + "react-refresh": "^0.14.0" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + }, + "peerDependencies": { + "@babel/core": "*" + } + }, + "node_modules/@react-native/metro-babel-transformer/node_modules/@react-native/codegen": { + "version": "0.85.3", + "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.85.3.tgz", + "integrity": "sha512-/JkS1lGLyzBWP1FbgDwaqEf7qShIC6pUC1M0a/YMAd/v4iqR24MRkQWe7jkYvcBQ2LpEhs5NGE9InhxSv21zCA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/parser": "^7.29.0", + "hermes-parser": "0.33.3", + "invariant": "^2.2.4", + "nullthrows": "^1.1.1", + "tinyglobby": "^0.2.15", + "yargs": "^17.6.2" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + }, + "peerDependencies": { + "@babel/core": "*" + } + }, + "node_modules/@react-native/metro-babel-transformer/node_modules/babel-plugin-syntax-hermes-parser": { + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.33.3.tgz", + "integrity": "sha512-/Z9xYdaJ1lC0pT9do6TqCqhOSLfZ5Ot8D5za1p+feEfWYupCOfGbhhEXN9r2ZgJtDNUNRw/Z+T2CvAGKBqtqWA==", + "license": "MIT", + "peer": true, + "dependencies": { + "hermes-parser": "0.33.3" + } + }, + "node_modules/@react-native/metro-babel-transformer/node_modules/hermes-estree": { + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.33.3.tgz", + "integrity": "sha512-6kzYZHCk8Fy1Uc+t3HGYyJn3OL4aeqKLTyina4UFtWl8I0kSL7OmKThaiX+Uh2f8nGw3mo4Ifxg0M5Zk3/Oeqg==", + "license": "MIT", + "peer": true + }, + "node_modules/@react-native/metro-babel-transformer/node_modules/hermes-parser": { + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.33.3.tgz", + "integrity": "sha512-Yg3HgaG4CqgyowtYjX/FsnPAuZdHOqSMtnbpylbptsQ9nwwSKsy6uRWcGO5RK0EqiX12q8HvDWKgeAVajRO5DA==", + "license": "MIT", + "peer": true, + "dependencies": { + "hermes-estree": "0.33.3" + } + }, + "node_modules/@react-native/metro-config": { + "version": "0.85.3", + "resolved": "https://registry.npmjs.org/@react-native/metro-config/-/metro-config-0.85.3.tgz", + "integrity": "sha512-sVo6HepUmCcpdfozEf91lA0FjpLNNZYu/Zi9FiYiAQTK8pzATXDVTqhvdxpFrQn435p5eUTSbllvbH/KN+bnyA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@react-native/js-polyfills": "0.85.3", + "@react-native/metro-babel-transformer": "0.85.3", + "metro-config": "^0.84.3", + "metro-runtime": "^0.84.3" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/@react-native/metro-config/node_modules/@react-native/js-polyfills": { + "version": "0.85.3", + "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.85.3.tgz", + "integrity": "sha512-U2+aMshIXf1uFn77tpBb/xhHWB9vkVrMpt7kkucAugF8hJKYTDGB587X7WwelHduK2KBfhl4giSv0rzZGoef9A==", + "license": "MIT", + "peer": true, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/@react-native/metro-config/node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "peer": true, + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@react-native/metro-config/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@react-native/metro-config/node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "license": "MIT", + "peer": true + }, + "node_modules/@react-native/metro-config/node_modules/hermes-estree": { + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.35.0.tgz", + "integrity": "sha512-xVx5Opwy8Oo1I5yGpVRhCvWL/iV3M+ylksSKVNlxxD90cpDpR/AR1jLYqK8HWihm065a6UI3HeyAmYzwS8NOOg==", + "license": "MIT", + "peer": true + }, + "node_modules/@react-native/metro-config/node_modules/hermes-parser": { + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.35.0.tgz", + "integrity": "sha512-9JLjeHxBx8T4CAsydZR49PNZUaix+WpQJwu9p2010lu+7Kwl6D/7wYFFJxoz+aXkaaClp9Zfg6W6/zVlSJORaA==", + "license": "MIT", + "peer": true, + "dependencies": { + "hermes-estree": "0.35.0" + } + }, + "node_modules/@react-native/metro-config/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "peer": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@react-native/metro-config/node_modules/metro": { + "version": "0.84.4", + "resolved": "https://registry.npmjs.org/metro/-/metro-0.84.4.tgz", + "integrity": "sha512-8ETTubqfD6ornDy2zYDvRcKnVDOXdFJsjetYDBsY4oAsb6NJkiwFR+FaMESyGppFmQUyBQA4H4sFGxzcQSGtFA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/core": "^7.25.2", + "@babel/generator": "^7.29.1", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "accepts": "^2.0.0", + "ci-info": "^2.0.0", + "connect": "^3.6.5", + "debug": "^4.4.0", + "error-stack-parser": "^2.0.6", + "flow-enums-runtime": "^0.0.6", + "graceful-fs": "^4.2.4", + "hermes-parser": "0.35.0", + "image-size": "^1.0.2", + "invariant": "^2.2.4", + "jest-worker": "^29.7.0", + "jsc-safe-url": "^0.2.2", + "lodash.throttle": "^4.1.1", + "metro-babel-transformer": "0.84.4", + "metro-cache": "0.84.4", + "metro-cache-key": "0.84.4", + "metro-config": "0.84.4", + "metro-core": "0.84.4", + "metro-file-map": "0.84.4", + "metro-resolver": "0.84.4", + "metro-runtime": "0.84.4", + "metro-source-map": "0.84.4", + "metro-symbolicate": "0.84.4", + "metro-transform-plugins": "0.84.4", + "metro-transform-worker": "0.84.4", + "mime-types": "^3.0.1", + "nullthrows": "^1.1.1", + "serialize-error": "^2.1.0", + "source-map": "^0.5.6", + "throat": "^5.0.0", + "ws": "^7.5.10", + "yargs": "^17.6.2" + }, + "bin": { + "metro": "src/cli.js" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/@react-native/metro-config/node_modules/metro-babel-transformer": { + "version": "0.84.4", + "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.84.4.tgz", + "integrity": "sha512-rvCfz8snl9h20VcvpOHxZuHP1SlAkv4HXbzw7nyyVwu6Eqo5PRerbakQ9XmUCOsRy70spJ37O+G1TK8oMzo48g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/core": "^7.25.2", + "flow-enums-runtime": "^0.0.6", + "hermes-parser": "0.35.0", + "metro-cache-key": "0.84.4", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/@react-native/metro-config/node_modules/metro-cache": { + "version": "0.84.4", + "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.84.4.tgz", + "integrity": "sha512-gpcFQdSLUwUCk71saKoE64jLFbx2nwTfVCcPSULMNT8QYq0p1eZZE29Jvd0HtT/UlhC3ZOutLxJME5xqD2JUZg==", + "license": "MIT", + "peer": true, + "dependencies": { + "exponential-backoff": "^3.1.1", + "flow-enums-runtime": "^0.0.6", + "https-proxy-agent": "^7.0.5", + "metro-core": "0.84.4" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/@react-native/metro-config/node_modules/metro-cache-key": { + "version": "0.84.4", + "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.84.4.tgz", + "integrity": "sha512-wVO79aGrkYImpnaVS4+d5RrRBRPX31QtvKB3wKGBuiNSznduZTQHzsrJZRroFJSwnygrzdsGUtDQPuqqFjFdvw==", + "license": "MIT", + "peer": true, + "dependencies": { + "flow-enums-runtime": "^0.0.6" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/@react-native/metro-config/node_modules/metro-config": { + "version": "0.84.4", + "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.84.4.tgz", + "integrity": "sha512-PMotGDjXcXLWo2TMRH+VR99phFNgYTwqh4OoieIKK3yTJa1Jmkl+fZJxDO0jfBvNF+WESHciHvpNuBtXaF3B0Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "connect": "^3.6.5", + "flow-enums-runtime": "^0.0.6", + "jest-validate": "^29.7.0", + "metro": "0.84.4", + "metro-cache": "0.84.4", + "metro-core": "0.84.4", + "metro-runtime": "0.84.4", + "yaml": "^2.6.1" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/@react-native/metro-config/node_modules/metro-core": { + "version": "0.84.4", + "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.84.4.tgz", + "integrity": "sha512-HONpWC5LGXZn3ffkd4Hu6AIrfE7j4Z0g0wMo/goV24WOB3lhuFZ40KgvaDiSw8iyQHloMYay5N/wPX+z8oN/PQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "flow-enums-runtime": "^0.0.6", + "lodash.throttle": "^4.1.1", + "metro-resolver": "0.84.4" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/@react-native/metro-config/node_modules/metro-file-map": { + "version": "0.84.4", + "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.84.4.tgz", + "integrity": "sha512-KSVDi/u60hKPx++NLu3MTIvyjzNoJnFAF8PQFxaj1jiSka/wjw+Ua6sNuJ0TDHQv+7AAoFQxeMgaRAe8Yic5wQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "debug": "^4.4.0", + "fb-watchman": "^2.0.0", + "flow-enums-runtime": "^0.0.6", + "graceful-fs": "^4.2.4", + "invariant": "^2.2.4", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "nullthrows": "^1.1.1", + "walker": "^1.0.7" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/@react-native/metro-config/node_modules/metro-minify-terser": { + "version": "0.84.4", + "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.84.4.tgz", + "integrity": "sha512-5qpbaVOMC7CPitIpuewzVeGw7E+C3ykbv2mqTjQLl85Z3annSVGlSCTcsZjqXZzjupfK4Ztj3dDc4kc44NZwtQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "flow-enums-runtime": "^0.0.6", + "terser": "^5.15.0" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/@react-native/metro-config/node_modules/metro-resolver": { + "version": "0.84.4", + "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.84.4.tgz", + "integrity": "sha512-1qLgbxQ5ZGhhutuPot1Yp348ofDsATL2WkrHF65TobqTT9K3P9qJXw38bomk7ncp5B7OYMfWwtyBZo1lCV792A==", + "license": "MIT", + "peer": true, + "dependencies": { + "flow-enums-runtime": "^0.0.6" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/@react-native/metro-config/node_modules/metro-runtime": { + "version": "0.84.4", + "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.84.4.tgz", + "integrity": "sha512-Jibypds4g7AhzdRKY+kDoj51s5EXMwgyp5ddtlreDAsWefMdOx+agWqgm0H2XSZ/ueanHHVM89fnf5OJnlxa8Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.25.0", + "flow-enums-runtime": "^0.0.6" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/@react-native/metro-config/node_modules/metro-source-map": { + "version": "0.84.4", + "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.84.4.tgz", + "integrity": "sha512-jbWkPxIesVuo1IWkvezmMJld6iu8nD62GsrZiV6jP37AOdbo4OBq1FJ+qkOg8sV05wAHB//jAbziuW0SlJfW4g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "flow-enums-runtime": "^0.0.6", + "invariant": "^2.2.4", + "metro-symbolicate": "0.84.4", + "nullthrows": "^1.1.1", + "ob1": "0.84.4", + "source-map": "^0.5.6", + "vlq": "^1.0.0" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/@react-native/metro-config/node_modules/metro-symbolicate": { + "version": "0.84.4", + "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.84.4.tgz", + "integrity": "sha512-OnfpacxUqGPZQ27t8qK9mFa7uqHIlVWeqRqkCbvMvreEBiamEeOn8krKtcwgP5M4cYDPwuSmCTopHMVthqG4zA==", + "license": "MIT", + "peer": true, + "dependencies": { + "flow-enums-runtime": "^0.0.6", + "invariant": "^2.2.4", + "metro-source-map": "0.84.4", + "nullthrows": "^1.1.1", + "source-map": "^0.5.6", + "vlq": "^1.0.0" + }, + "bin": { + "metro-symbolicate": "src/index.js" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/@react-native/metro-config/node_modules/metro-transform-plugins": { + "version": "0.84.4", + "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.84.4.tgz", + "integrity": "sha512-kehr6HbAecqD0/a3xLXobELdPaAmRAl8bel0qagPF4vhZtux93nS8S4eq2kgKt6J2GnQpVjSoW1PXdst04mwow==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/generator": "^7.29.1", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "flow-enums-runtime": "^0.0.6", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/@react-native/metro-config/node_modules/metro-transform-worker": { + "version": "0.84.4", + "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.84.4.tgz", + "integrity": "sha512-W1IYMvvXTu4MxYr7d9h7CeG2vpIr3bmLLIavkPY4O1ilzDrvS8z/NEe6y+pC44Ff7raMXQgYSfdqDUwN/i39gg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/generator": "^7.29.1", + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "flow-enums-runtime": "^0.0.6", + "metro": "0.84.4", + "metro-babel-transformer": "0.84.4", + "metro-cache": "0.84.4", + "metro-cache-key": "0.84.4", + "metro-minify-terser": "0.84.4", + "metro-source-map": "0.84.4", + "metro-transform-plugins": "0.84.4", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/@react-native/metro-config/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "peer": true, + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@react-native/metro-config/node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@react-native/metro-config/node_modules/ob1": { + "version": "0.84.4", + "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.84.4.tgz", + "integrity": "sha512-eJXMpz4aQHXF/YBB9ddqZDIS+ooO91hObo9FoW/xBkr54/zCwYYCDqT/O54vNo8kOkWs5Ou/y28NgdrV0edQNA==", + "license": "MIT", + "peer": true, + "dependencies": { + "flow-enums-runtime": "^0.0.6" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/@react-native/metro-config/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@react-native/metro-config/node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "license": "ISC", + "peer": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/@react-native/normalize-colors": { + "version": "0.81.5", + "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.81.5.tgz", + "integrity": "sha512-0HuJ8YtqlTVRXGZuGeBejLE04wSQsibpTI+RGOyVqxZvgtlLLC/Ssw0UmbHhT4lYMp2fhdtvKZSs5emWB1zR/g==", + "license": "MIT" }, "node_modules/@react-native/virtualized-lists": { "version": "0.72.8", @@ -5546,15 +6336,28 @@ "node": "^14.21.3 || >=16" }, "funding": { - "url": "https://paulmillr.com/funding/" + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.10", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", + "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", + "license": "MIT" + }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" } }, - "node_modules/@sinclair/typebox": { - "version": "0.27.10", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", - "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", - "license": "MIT" - }, "node_modules/@sinonjs/commons": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", @@ -5914,106 +6717,433 @@ "integrity": "sha512-ZhpZtD+4VArf6RPitsVExvgkF+nGghd1rzPjd97GmBximpnt1rsUxMOEyoIEuH3XBxPyNB6Us7ha7RHWQR+abg==", "license": "Apache-2.0", "dependencies": { - "@wallet-standard/base": "^1.1.0", - "@wallet-standard/features": "^1.1.0" + "@wallet-standard/base": "^1.1.0", + "@wallet-standard/features": "^1.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@solana/wallet-standard-util": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@solana/wallet-standard-util/-/wallet-standard-util-1.1.2.tgz", + "integrity": "sha512-rUXFNP4OY81Ddq7qOjQV4Kmkozx4wjYAxljvyrqPx8Ycz0FYChG/hQVWqvgpK3sPsEaO/7ABG1NOACsyAKWNOA==", + "license": "Apache-2.0", + "dependencies": { + "@noble/curves": "^1.8.0", + "@solana/wallet-standard-chains": "^1.1.1", + "@solana/wallet-standard-features": "^1.3.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@solana/wallet-standard-wallet-adapter": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@solana/wallet-standard-wallet-adapter/-/wallet-standard-wallet-adapter-1.1.4.tgz", + "integrity": "sha512-YSBrxwov4irg2hx9gcmM4VTew3ofNnkqsXQ42JwcS6ykF1P1ecVY8JCbrv75Nwe6UodnqeoZRbN7n/p3awtjNQ==", + "license": "Apache-2.0", + "dependencies": { + "@solana/wallet-standard-wallet-adapter-base": "^1.1.4", + "@solana/wallet-standard-wallet-adapter-react": "^1.1.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@solana/wallet-standard-wallet-adapter-base": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@solana/wallet-standard-wallet-adapter-base/-/wallet-standard-wallet-adapter-base-1.1.4.tgz", + "integrity": "sha512-Q2Rie9YaidyFA4UxcUIxUsvynW+/gE2noj/Wmk+IOwDwlVrJUAXCvFaCNsPDSyKoiYEKxkSnlG13OA1v08G4iw==", + "license": "Apache-2.0", + "dependencies": { + "@solana/wallet-adapter-base": "^0.9.23", + "@solana/wallet-standard-chains": "^1.1.1", + "@solana/wallet-standard-features": "^1.3.0", + "@solana/wallet-standard-util": "^1.1.2", + "@wallet-standard/app": "^1.1.0", + "@wallet-standard/base": "^1.1.0", + "@wallet-standard/features": "^1.1.0", + "@wallet-standard/wallet": "^1.1.0" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@solana/web3.js": "^1.98.0", + "bs58": "^6.0.0" + } + }, + "node_modules/@solana/wallet-standard-wallet-adapter-react": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@solana/wallet-standard-wallet-adapter-react/-/wallet-standard-wallet-adapter-react-1.1.4.tgz", + "integrity": "sha512-xa4KVmPgB7bTiWo4U7lg0N6dVUtt2I2WhEnKlIv0jdihNvtyhOjCKMjucWet6KAVhir6I/mSWrJk1U9SvVvhCg==", + "license": "Apache-2.0", + "dependencies": { + "@solana/wallet-standard-wallet-adapter-base": "^1.1.4", + "@wallet-standard/app": "^1.1.0", + "@wallet-standard/base": "^1.1.0" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@solana/wallet-adapter-base": "*", + "react": "*" + } + }, + "node_modules/@stablelib/base64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz", + "integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==", + "license": "MIT" + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "license": "MIT" + }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, + "node_modules/@stripe/stripe-js": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-5.6.0.tgz", + "integrity": "sha512-w8CEY73X/7tw2KKlL3iOk679V9bWseE4GzNz3zlaYxcTjmcmWOathRb0emgo/QQ3eoNzmq68+2Y2gxluAv3xGw==", + "license": "MIT", + "engines": { + "node": ">=12.16" + } + }, + "node_modules/@svgr/babel-plugin-add-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", + "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", + "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-dynamic-title": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", + "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-em-dimensions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", + "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-react-native-svg": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", + "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-svg-component": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", + "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-preset": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", + "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", + "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", + "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", + "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", + "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", + "@svgr/babel-plugin-transform-svg-component": "8.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/core": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", + "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^8.1.3", + "snake-case": "^3.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/core/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@svgr/core/node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" }, "engines": { - "node": ">=16" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@solana/wallet-standard-util": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@solana/wallet-standard-util/-/wallet-standard-util-1.1.2.tgz", - "integrity": "sha512-rUXFNP4OY81Ddq7qOjQV4Kmkozx4wjYAxljvyrqPx8Ycz0FYChG/hQVWqvgpK3sPsEaO/7ABG1NOACsyAKWNOA==", - "license": "Apache-2.0", + "node_modules/@svgr/hast-util-to-babel-ast": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", + "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", + "dev": true, + "license": "MIT", "dependencies": { - "@noble/curves": "^1.8.0", - "@solana/wallet-standard-chains": "^1.1.1", - "@solana/wallet-standard-features": "^1.3.0" + "@babel/types": "^7.21.3", + "entities": "^4.4.0" }, "engines": { - "node": ">=16" + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" } }, - "node_modules/@solana/wallet-standard-wallet-adapter": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@solana/wallet-standard-wallet-adapter/-/wallet-standard-wallet-adapter-1.1.4.tgz", - "integrity": "sha512-YSBrxwov4irg2hx9gcmM4VTew3ofNnkqsXQ42JwcS6ykF1P1ecVY8JCbrv75Nwe6UodnqeoZRbN7n/p3awtjNQ==", - "license": "Apache-2.0", - "dependencies": { - "@solana/wallet-standard-wallet-adapter-base": "^1.1.4", - "@solana/wallet-standard-wallet-adapter-react": "^1.1.4" - }, + "node_modules/@svgr/hast-util-to-babel-ast/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", "engines": { - "node": ">=16" + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/@solana/wallet-standard-wallet-adapter-base": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@solana/wallet-standard-wallet-adapter-base/-/wallet-standard-wallet-adapter-base-1.1.4.tgz", - "integrity": "sha512-Q2Rie9YaidyFA4UxcUIxUsvynW+/gE2noj/Wmk+IOwDwlVrJUAXCvFaCNsPDSyKoiYEKxkSnlG13OA1v08G4iw==", - "license": "Apache-2.0", + "node_modules/@svgr/plugin-jsx": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", + "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", + "dev": true, + "license": "MIT", "dependencies": { - "@solana/wallet-adapter-base": "^0.9.23", - "@solana/wallet-standard-chains": "^1.1.1", - "@solana/wallet-standard-features": "^1.3.0", - "@solana/wallet-standard-util": "^1.1.2", - "@wallet-standard/app": "^1.1.0", - "@wallet-standard/base": "^1.1.0", - "@wallet-standard/features": "^1.1.0", - "@wallet-standard/wallet": "^1.1.0" + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "@svgr/hast-util-to-babel-ast": "8.0.0", + "svg-parser": "^2.0.4" }, "engines": { - "node": ">=16" + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" }, "peerDependencies": { - "@solana/web3.js": "^1.98.0", - "bs58": "^6.0.0" + "@svgr/core": "*" } }, - "node_modules/@solana/wallet-standard-wallet-adapter-react": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@solana/wallet-standard-wallet-adapter-react/-/wallet-standard-wallet-adapter-react-1.1.4.tgz", - "integrity": "sha512-xa4KVmPgB7bTiWo4U7lg0N6dVUtt2I2WhEnKlIv0jdihNvtyhOjCKMjucWet6KAVhir6I/mSWrJk1U9SvVvhCg==", - "license": "Apache-2.0", + "node_modules/@svgr/plugin-svgo": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz", + "integrity": "sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==", + "dev": true, + "license": "MIT", "dependencies": { - "@solana/wallet-standard-wallet-adapter-base": "^1.1.4", - "@wallet-standard/app": "^1.1.0", - "@wallet-standard/base": "^1.1.0" + "cosmiconfig": "^8.1.3", + "deepmerge": "^4.3.1", + "svgo": "^3.0.2" }, "engines": { - "node": ">=16" + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" }, "peerDependencies": { - "@solana/wallet-adapter-base": "*", - "react": "*" + "@svgr/core": "*" } }, - "node_modules/@stablelib/base64": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz", - "integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==", - "license": "MIT" - }, - "node_modules/@standard-schema/spec": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", - "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "license": "MIT" - }, - "node_modules/@standard-schema/utils": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", - "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", - "license": "MIT" - }, - "node_modules/@stripe/stripe-js": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-5.6.0.tgz", - "integrity": "sha512-w8CEY73X/7tw2KKlL3iOk679V9bWseE4GzNz3zlaYxcTjmcmWOathRb0emgo/QQ3eoNzmq68+2Y2gxluAv3xGw==", + "node_modules/@svgr/plugin-svgo/node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, "license": "MIT", + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, "engines": { - "node": ">=12.16" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/@swc/helpers": { @@ -6025,6 +7155,19 @@ "tslib": "^2.8.0" } }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dev": true, + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@tanstack/query-core": { "version": "5.87.4", "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.87.4.tgz", @@ -6045,6 +7188,34 @@ "node": ">= 10" } }, + "node_modules/@tsconfig/node10": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", + "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, "node_modules/@tybys/wasm-util": { "version": "0.10.2", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", @@ -6115,6 +7286,19 @@ "@types/node": "*" } }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -6196,6 +7380,13 @@ "integrity": "sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw==", "license": "MIT" }, + "node_modules/@types/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/http-errors": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", @@ -6275,6 +7466,16 @@ "@types/node": "*" } }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/methods": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", @@ -6356,7 +7557,7 @@ "version": "19.1.17", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.17.tgz", "integrity": "sha512-Qec1E3mhALmaspIrhWt9jkQMNdw6bReVu64mjvhbhq2NFPftLPVr+l1SZgmw/66WwBNpDh7ao5AT6gF5v41PFA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "csstype": "^3.0.2" @@ -6373,6 +7574,16 @@ "@types/react": "*" } }, + "node_modules/@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/send": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", @@ -8280,6 +9491,51 @@ "node": ">= 0.8" } }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/call-bind": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", @@ -8519,6 +9775,19 @@ "node": ">=0.8" } }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/clsx": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", @@ -8894,6 +10163,13 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, "node_modules/cross-fetch": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", @@ -8982,6 +10258,42 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-tree": "~2.2.0" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", + "dev": true, + "license": "CC0-1.0" + }, "node_modules/cssom": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", @@ -9143,6 +10455,35 @@ "node": ">=0.10" } }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/dedent": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", @@ -9195,6 +10536,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -9311,6 +10662,16 @@ "wrappy": "1" } }, + "node_modules/diff": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/diff-sequences": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", @@ -9434,6 +10795,17 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/dotenv": { "version": "16.6.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", @@ -9530,6 +10902,16 @@ "node": ">= 0.8" } }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/entities": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", @@ -10977,6 +12359,36 @@ "react-native": "*" } }, + "node_modules/expo-auth-session": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/expo-auth-session/-/expo-auth-session-7.0.11.tgz", + "integrity": "sha512-AhWtt/m9rb1Po77X/VBFbeE6UTgbm2vXP2iCblUSRsHCw2qD6lO0ulKUB8Xyxy9FtoI9yrNQ1iwCNgIIgo8VYQ==", + "license": "MIT", + "dependencies": { + "expo-application": "~7.0.8", + "expo-constants": "~18.0.13", + "expo-crypto": "~15.0.9", + "expo-linking": "~8.0.12", + "expo-web-browser": "~15.0.11", + "invariant": "^2.2.4" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, + "node_modules/expo-auth-session/node_modules/expo-crypto": { + "version": "15.0.9", + "resolved": "https://registry.npmjs.org/expo-crypto/-/expo-crypto-15.0.9.tgz", + "integrity": "sha512-SNWKa2fXx7v9gkp1h/7nqXY5XN7qgNDn3yRc2aO0gWGbeMbvob/haMxxsPFe9f51aqH5NjNCqHf2kvLhvAd8KQ==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.0" + }, + "peerDependencies": { + "expo": "*" + } + }, "node_modules/expo-camera": { "version": "17.0.10", "resolved": "https://registry.npmjs.org/expo-camera/-/expo-camera-17.0.10.tgz", @@ -11267,6 +12679,15 @@ "node": ">=10" } }, + "node_modules/expo-secure-store": { + "version": "15.0.8", + "resolved": "https://registry.npmjs.org/expo-secure-store/-/expo-secure-store-15.0.8.tgz", + "integrity": "sha512-lHnzvRajBu4u+P99+0GEMijQMFCOYpWRO4dWsXSuMt77+THPIGjzNvVKrGSl6mMrLsfVaKL8BpwYZLGlgA+zAw==", + "license": "MIT", + "peerDependencies": { + "expo": "*" + } + }, "node_modules/expo-sensors": { "version": "15.0.8", "resolved": "https://registry.npmjs.org/expo-sensors/-/expo-sensors-15.0.8.tgz", @@ -12145,6 +13566,32 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -12356,6 +13803,13 @@ "dev": true, "license": "MIT" }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "dev": true, + "license": "BSD-2-Clause" + }, "node_modules/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", @@ -12391,6 +13845,20 @@ "node": ">= 6" } }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -14929,6 +16397,26 @@ } } }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -15492,6 +16980,16 @@ "node": ">=6" } }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/minimatch": { "version": "9.0.9", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", @@ -15713,6 +17211,17 @@ "integrity": "sha512-SrQrok4CATudVzBS7coSz26QRSmlK9TzzoFbeKfcPBUFPjcQM9Rqvr/DlJkOrwI/0KcgvMub1n1g5Jt9EgRn4A==", "license": "MIT" }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, "node_modules/node-cron": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.3.tgz", @@ -15836,6 +17345,19 @@ "node": ">=0.10.0" } }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/npm-package-arg": { "version": "11.0.3", "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.3.tgz", @@ -16293,6 +17815,16 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -16397,6 +17929,13 @@ "node": ">= 0.8" } }, + "node_modules/path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==", + "dev": true, + "license": "MIT" + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -16812,7 +18351,7 @@ "version": "5.22.0", "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.22.0.tgz", "integrity": "sha512-vtpjW3XuYCSnMsNVBjLMNkTj6OZbudcPPTPYHqX0CJfpcdWciI1dM8uHETwmDxxiqEwCIE6WvXucWUetJgfu/A==", - "dev": true, + "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -16926,6 +18465,17 @@ "url": "https://github.com/sponsors/lupomontero" } }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -17183,6 +18733,19 @@ ], "license": "MIT" }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -17308,19 +18871,19 @@ "license": "MIT" }, "node_modules/react-native": { - "version": "0.81.4", - "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.81.4.tgz", - "integrity": "sha512-bt5bz3A/+Cv46KcjV0VQa+fo7MKxs17RCcpzjftINlen4ZDUl0I6Ut+brQ2FToa5oD0IB0xvQHfmsg2EDqsZdQ==", + "version": "0.81.5", + "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.81.5.tgz", + "integrity": "sha512-1w+/oSjEXZjMqsIvmkCRsOc8UBYv163bTWKTI8+1mxztvQPhCRYGTvZ/PL1w16xXHneIj/SLGfxWg2GWN2uexw==", "license": "MIT", "dependencies": { "@jest/create-cache-key-function": "^29.7.0", - "@react-native/assets-registry": "0.81.4", - "@react-native/codegen": "0.81.4", - "@react-native/community-cli-plugin": "0.81.4", - "@react-native/gradle-plugin": "0.81.4", - "@react-native/js-polyfills": "0.81.4", - "@react-native/normalize-colors": "0.81.4", - "@react-native/virtualized-lists": "0.81.4", + "@react-native/assets-registry": "0.81.5", + "@react-native/codegen": "0.81.5", + "@react-native/community-cli-plugin": "0.81.5", + "@react-native/gradle-plugin": "0.81.5", + "@react-native/js-polyfills": "0.81.5", + "@react-native/normalize-colors": "0.81.5", + "@react-native/virtualized-lists": "0.81.5", "abort-controller": "^3.0.0", "anser": "^1.4.9", "ansi-regex": "^5.0.0", @@ -17551,6 +19114,7 @@ "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.15.4.tgz", "integrity": "sha512-boT/vIRgj6zZKBpfTPJJiYWMbZE9duBMOwPK6kCSTgxsS947IFMOq9OgIFkpWZTB7t229H24pDRkh3W9ZK/J1A==", "license": "MIT", + "peer": true, "dependencies": { "css-select": "^5.1.0", "css-tree": "^1.1.3", @@ -17561,6 +19125,23 @@ "react-native": "*" } }, + "node_modules/react-native-svg-transformer": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/react-native-svg-transformer/-/react-native-svg-transformer-1.5.3.tgz", + "integrity": "sha512-M4uFg5pUt35OMgjD4rWWbwd6PmxV96W7r/gQTTa+iZA5B+jO6aURhzAZGLHSrg1Kb91cKG0Rildy9q1WJvYstg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@svgr/core": "^8.1.0", + "@svgr/plugin-jsx": "^8.1.0", + "@svgr/plugin-svgo": "^8.1.0", + "path-dirname": "^1.0.2" + }, + "peerDependencies": { + "react-native": ">=0.59.0", + "react-native-svg": ">=12.0.0" + } + }, "node_modules/react-native-url-polyfill": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/react-native-url-polyfill/-/react-native-url-polyfill-2.0.0.tgz", @@ -17605,37 +19186,41 @@ "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", "license": "MIT" }, - "node_modules/react-native/node_modules/@react-native/codegen": { - "version": "0.81.4", - "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.81.4.tgz", - "integrity": "sha512-LWTGUTzFu+qOQnvkzBP52B90Ym3stZT8IFCzzUrppz8Iwglg83FCtDZAR4yLHI29VY/x/+pkcWAMCl3739XHdw==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.25.2", - "@babel/parser": "^7.25.3", - "glob": "^7.1.1", - "hermes-parser": "0.29.1", - "invariant": "^2.2.4", - "nullthrows": "^1.1.1", - "yargs": "^17.6.2" - }, - "engines": { - "node": ">= 20.19.4" + "node_modules/react-native-worklets": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/react-native-worklets/-/react-native-worklets-0.8.3.tgz", + "integrity": "sha512-oCBJROyLU7yG/1R8s0INMflygTH71bx+5XcYkH0CM938TlhSoVbiunE1WVW5FZa51vwYqfLie/IXMX2s1Kh3eg==", + "license": "MIT", + "dependencies": { + "@babel/plugin-transform-arrow-functions": "^7.27.1", + "@babel/plugin-transform-class-properties": "^7.27.1", + "@babel/plugin-transform-classes": "^7.28.4", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1", + "@babel/plugin-transform-shorthand-properties": "^7.27.1", + "@babel/plugin-transform-template-literals": "^7.27.1", + "@babel/plugin-transform-unicode-regex": "^7.27.1", + "@babel/preset-typescript": "^7.27.1", + "convert-source-map": "^2.0.0", + "semver": "^7.7.3" }, "peerDependencies": { - "@babel/core": "*" + "@babel/core": "*", + "@react-native/metro-config": "*", + "react": "*", + "react-native": "0.81 - 0.85" } }, - "node_modules/react-native/node_modules/@react-native/normalize-colors": { - "version": "0.81.4", - "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.81.4.tgz", - "integrity": "sha512-9nRRHO1H+tcFqjb9gAM105Urtgcanbta2tuqCVY0NATHeFPDEAB7gPyiLxCHKMi1NbhP6TH0kxgSWXKZl1cyRg==", + "node_modules/react-native-worklets/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "license": "MIT" }, "node_modules/react-native/node_modules/@react-native/virtualized-lists": { - "version": "0.81.4", - "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.81.4.tgz", - "integrity": "sha512-hBM+rMyL6Wm1Q4f/WpqGsaCojKSNUBqAXLABNGoWm1vabZ7cSnARMxBvA/2vo3hLcoR4v7zDK8tkKm9+O0LjVA==", + "version": "0.81.5", + "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.81.5.tgz", + "integrity": "sha512-UVXgV/db25OPIvwZySeToXD/9sKKhOdkcWmmf4Jh8iBZuyfML+/5CasaZ1E7Lqg6g3uqVQq75NqIwkYmORJMPw==", "license": "MIT", "dependencies": { "invariant": "^2.2.4", @@ -18065,6 +19650,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true, + "license": "MIT" + }, "node_modules/resolve-cwd": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", @@ -18112,6 +19704,19 @@ "node": ">=10" } }, + "node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/restore-cursor": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", @@ -18716,6 +20321,17 @@ "node": ">=8.0.0" } }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -19292,6 +20908,70 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/svgo": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.3.tgz", + "integrity": "sha512-+wn7I4p7YgJhHs38k2TNjy1vCfPIfLIJWR5MnCStsN8WuuTcBnRKcMHQLMM2ijxGZmDoZwNv8ipl5aTTen62ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^7.2.0", + "css-select": "^5.1.0", + "css-tree": "^2.3.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.0.0", + "sax": "^1.5.0" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/svgo/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/svgo/node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/svgo/node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "dev": true, + "license": "CC0-1.0" + }, "node_modules/swr": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/swr/-/swr-2.3.4.tgz", @@ -19664,6 +21344,57 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, "node_modules/tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -19861,7 +21592,7 @@ "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -20193,6 +21924,13 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, "node_modules/v8-to-istanbul": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", @@ -20899,6 +22637,16 @@ "node": ">=12" } }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..0e6371f --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,4 @@ +{ + "compilerOptions": {}, + "extends": "expo/tsconfig.base" +}