Spaces:
Sleeping
Sleeping
Green Doctor Deployer commited on
Commit ·
a112f73
1
Parent(s): b97e578
Environment: Optimize for Hugging Face Spaces and update documentation
Browse files- app.json +4 -4
- backend/__pycache__/__init__.cpython-312.pyc +0 -0
- backend/__pycache__/app.cpython-312.pyc +0 -0
- backend/app.py +4 -2
- backend/test_rejection.py +35 -0
- constants/theme.js +48 -27
- constants/translations.js +43 -22
- screens/AboutScreen.js +195 -167
- screens/HistoryScreen.js +160 -67
- screens/HomeScreen.js +385 -272
- screens/LanguageScreen.js +176 -52
- screens/ResultScreen.js +461 -231
- screens/ScannerScreen.js +397 -179
- screens/SchemesScreen.js +270 -134
- services/aiService.js +8 -3
- test_image.txt +3 -0
app.json
CHANGED
|
@@ -4,11 +4,11 @@
|
|
| 4 |
"slug": "GreenDoctor",
|
| 5 |
"version": "1.0.0",
|
| 6 |
"orientation": "portrait",
|
| 7 |
-
"icon": "./assets/
|
| 8 |
"userInterfaceStyle": "light",
|
| 9 |
"newArchEnabled": true,
|
| 10 |
"splash": {
|
| 11 |
-
"image": "./assets/
|
| 12 |
"resizeMode": "contain",
|
| 13 |
"backgroundColor": "#ffffff"
|
| 14 |
},
|
|
@@ -17,7 +17,7 @@
|
|
| 17 |
},
|
| 18 |
"android": {
|
| 19 |
"adaptiveIcon": {
|
| 20 |
-
"foregroundImage": "./assets/
|
| 21 |
"backgroundColor": "#ffffff"
|
| 22 |
},
|
| 23 |
"edgeToEdgeEnabled": true,
|
|
@@ -28,7 +28,7 @@
|
|
| 28 |
]
|
| 29 |
},
|
| 30 |
"web": {
|
| 31 |
-
"favicon": "./assets/
|
| 32 |
},
|
| 33 |
"extra": {
|
| 34 |
"eas": {
|
|
|
|
| 4 |
"slug": "GreenDoctor",
|
| 5 |
"version": "1.0.0",
|
| 6 |
"orientation": "portrait",
|
| 7 |
+
"icon": "./assets/logo.png",
|
| 8 |
"userInterfaceStyle": "light",
|
| 9 |
"newArchEnabled": true,
|
| 10 |
"splash": {
|
| 11 |
+
"image": "./assets/logo.png",
|
| 12 |
"resizeMode": "contain",
|
| 13 |
"backgroundColor": "#ffffff"
|
| 14 |
},
|
|
|
|
| 17 |
},
|
| 18 |
"android": {
|
| 19 |
"adaptiveIcon": {
|
| 20 |
+
"foregroundImage": "./assets/logo.png",
|
| 21 |
"backgroundColor": "#ffffff"
|
| 22 |
},
|
| 23 |
"edgeToEdgeEnabled": true,
|
|
|
|
| 28 |
]
|
| 29 |
},
|
| 30 |
"web": {
|
| 31 |
+
"favicon": "./assets/logo.png"
|
| 32 |
},
|
| 33 |
"extra": {
|
| 34 |
"eas": {
|
backend/__pycache__/__init__.cpython-312.pyc
CHANGED
|
Binary files a/backend/__pycache__/__init__.cpython-312.pyc and b/backend/__pycache__/__init__.cpython-312.pyc differ
|
|
|
backend/__pycache__/app.cpython-312.pyc
CHANGED
|
Binary files a/backend/__pycache__/app.cpython-312.pyc and b/backend/__pycache__/app.cpython-312.pyc differ
|
|
|
backend/app.py
CHANGED
|
@@ -14,10 +14,12 @@ import gc
|
|
| 14 |
import torch
|
| 15 |
torch.set_num_threads(1)
|
| 16 |
|
| 17 |
-
#
|
| 18 |
IS_RENDER = os.environ.get("RENDER", "false").lower() == "true"
|
|
|
|
|
|
|
| 19 |
|
| 20 |
-
app = FastAPI(title="Green Doctor AI Vision")
|
| 21 |
|
| 22 |
app.add_middleware(
|
| 23 |
CORSMiddleware,
|
|
|
|
| 14 |
import torch
|
| 15 |
torch.set_num_threads(1)
|
| 16 |
|
| 17 |
+
# Environment Detection
|
| 18 |
IS_RENDER = os.environ.get("RENDER", "false").lower() == "true"
|
| 19 |
+
IS_HF = "SPACE_ID" in os.environ
|
| 20 |
+
ENV_MODE = "HuggingFace" if IS_HF else ("Render" if IS_RENDER else "Local")
|
| 21 |
|
| 22 |
+
app = FastAPI(title=f"Green Doctor AI Vision [{ENV_MODE}]")
|
| 23 |
|
| 24 |
app.add_middleware(
|
| 25 |
CORSMiddleware,
|
backend/test_rejection.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import requests
|
| 2 |
+
from PIL import Image
|
| 3 |
+
import io
|
| 4 |
+
import time
|
| 5 |
+
|
| 6 |
+
BASE_URL = "http://localhost:8000" # Test locally first
|
| 7 |
+
|
| 8 |
+
def test_rejection(color, name):
|
| 9 |
+
print(f"\n--- Testing {name} ({color}) ---")
|
| 10 |
+
img = Image.new('RGB', (224, 224), color=color)
|
| 11 |
+
img_byte_arr = io.BytesIO()
|
| 12 |
+
img.save(img_byte_arr, format='JPEG')
|
| 13 |
+
img_byte_arr = img_byte_arr.getvalue()
|
| 14 |
+
|
| 15 |
+
try:
|
| 16 |
+
response = requests.post(
|
| 17 |
+
f"{BASE_URL}/predict",
|
| 18 |
+
files={"file": ("test.jpg", img_byte_arr, "image/jpeg")}
|
| 19 |
+
)
|
| 20 |
+
if response.status_code == 200:
|
| 21 |
+
data = response.json()
|
| 22 |
+
print(f"Result: {data.get('class')}")
|
| 23 |
+
print(f"Details: {data.get('ai_details')}")
|
| 24 |
+
else:
|
| 25 |
+
print(f"Error: {response.status_code} - {response.text}")
|
| 26 |
+
except Exception as e:
|
| 27 |
+
print(f"Request failed: {e}")
|
| 28 |
+
|
| 29 |
+
# Color tests
|
| 30 |
+
# White (Non-plant)
|
| 31 |
+
test_rejection((255, 255, 255), "Pure White")
|
| 32 |
+
# Blue (Non-plant)
|
| 33 |
+
test_rejection((0, 0, 255), "Pure Blue")
|
| 34 |
+
# Bright Green (Likely Plant)
|
| 35 |
+
test_rejection((34, 139, 34), "Forest Green")
|
constants/theme.js
CHANGED
|
@@ -1,37 +1,58 @@
|
|
| 1 |
export const COLORS = {
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
error: '#D32F2F',
|
|
|
|
|
|
|
| 10 |
white: '#FFFFFF',
|
| 11 |
black: '#000000',
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
},
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
}
|
| 22 |
};
|
| 23 |
|
| 24 |
-
export const FONTS = {
|
| 25 |
-
regular: 'System',
|
| 26 |
-
bold: 'System',
|
| 27 |
-
// In a real app we'd load custom fonts here
|
| 28 |
-
};
|
| 29 |
-
|
| 30 |
export const SIZES = {
|
| 31 |
-
padding:
|
| 32 |
-
radius:
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
|
|
|
| 37 |
};
|
|
|
|
| 1 |
export const COLORS = {
|
| 2 |
+
// Brand Palette (HSL Derived for harmony)
|
| 3 |
+
primary: '#2E7D32', // Deep Forest Green
|
| 4 |
+
primaryLight: '#4CAF50', // Leaf Green
|
| 5 |
+
primaryDark: '#1B5E20', // Dark Moss
|
| 6 |
+
secondary: '#8BC34A', // Sprout Green
|
| 7 |
+
|
| 8 |
+
// Neutral Palette
|
| 9 |
+
background: '#F9FBF9', // Soft Mint-tinted White
|
| 10 |
+
surface: '#FFFFFF',
|
| 11 |
+
text: '#122613', // Almost Black Green
|
| 12 |
+
textLight: '#5C6B5D', // Muted Sage
|
| 13 |
+
border: '#E0EAE0',
|
| 14 |
+
|
| 15 |
+
// Status Palette
|
| 16 |
+
success: '#388E3C',
|
| 17 |
+
warning: '#F57C00',
|
| 18 |
error: '#D32F2F',
|
| 19 |
+
info: '#1976D2',
|
| 20 |
+
|
| 21 |
white: '#FFFFFF',
|
| 22 |
black: '#000000',
|
| 23 |
+
|
| 24 |
+
// Premium Layered Shadows
|
| 25 |
+
shadow: {
|
| 26 |
+
sm: {
|
| 27 |
+
shadowColor: "#000",
|
| 28 |
+
shadowOffset: { width: 0, height: 1 },
|
| 29 |
+
shadowOpacity: 0.1,
|
| 30 |
+
shadowRadius: 2,
|
| 31 |
+
elevation: 2,
|
| 32 |
},
|
| 33 |
+
md: {
|
| 34 |
+
shadowColor: "#1B5E20",
|
| 35 |
+
shadowOffset: { width: 0, height: 4 },
|
| 36 |
+
shadowOpacity: 0.08,
|
| 37 |
+
shadowRadius: 8,
|
| 38 |
+
elevation: 4,
|
| 39 |
+
},
|
| 40 |
+
lg: {
|
| 41 |
+
shadowColor: "#1B5E20",
|
| 42 |
+
shadowOffset: { width: 0, height: 10 },
|
| 43 |
+
shadowOpacity: 0.12,
|
| 44 |
+
shadowRadius: 20,
|
| 45 |
+
elevation: 8,
|
| 46 |
+
}
|
| 47 |
}
|
| 48 |
};
|
| 49 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
export const SIZES = {
|
| 51 |
+
padding: 20,
|
| 52 |
+
radius: 20, // Larger radius for modern look
|
| 53 |
+
radiusSm: 12,
|
| 54 |
+
h1: 28,
|
| 55 |
+
h2: 24,
|
| 56 |
+
h3: 18,
|
| 57 |
+
body: 16,
|
| 58 |
};
|
constants/translations.js
CHANGED
|
@@ -47,6 +47,11 @@ export const TRANSLATIONS = {
|
|
| 47 |
missionPoint3: "To promote sustainable agriculture practices in Puduvayal and beyond.",
|
| 48 |
changeLanguage: "Change Language",
|
| 49 |
aboutApp: "About App",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
govtSchemes: "Government Schemes",
|
| 51 |
viewSchemes: "View available schemes",
|
| 52 |
centralSchemes: "Central Schemes",
|
|
@@ -54,6 +59,25 @@ export const TRANSLATIONS = {
|
|
| 54 |
otherSchemes: "Other Schemes",
|
| 55 |
schemeDetails: "Scheme Details",
|
| 56 |
noSchemesAvailable: "No schemes available at the moment",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
ourTeam: "OUR TEAM",
|
| 58 |
principal: "Principal: Dr. K. Baskaran, Principal, ACGCET",
|
| 59 |
principalInvestigator: "Principal Investigator (PI): Dr. S. Nageswari, ASP/EEE",
|
|
@@ -99,28 +123,25 @@ export const TRANSLATIONS = {
|
|
| 99 |
noHistory: "வரலாறு இல்லை",
|
| 100 |
confirmDelete: "நிச்சயமாக இதை நீக்க விரும்புகிறீர்களா?",
|
| 101 |
cancel: "ரத்து செய்",
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
otherSchemes: "பிற திட்டங்கள்",
|
| 122 |
-
schemeDetails: "திட்ட விவரங்கள்",
|
| 123 |
-
noSchemesAvailable: "தற்போது எந்த திட்டங்களும் இல்லை",
|
| 124 |
ourTeam: "எங்கள் குழு",
|
| 125 |
principal: "முதல்வர்: Dr. K. பாஸ்கரன், முதல்வர், ACGCET",
|
| 126 |
principalInvestigator: "முதன்மை ஆய்வாளர் (PI): Dr. S. நாகேஸ்வரி, ASP/EEE",
|
|
|
|
| 47 |
missionPoint3: "To promote sustainable agriculture practices in Puduvayal and beyond.",
|
| 48 |
changeLanguage: "Change Language",
|
| 49 |
aboutApp: "About App",
|
| 50 |
+
analyseNow: "Analyse Now",
|
| 51 |
+
autoScan: "AUTO SCAN",
|
| 52 |
+
manualCapture: "MANUAL",
|
| 53 |
+
scanning: "Scanning...",
|
| 54 |
+
holdSteady: "Hold steady...",
|
| 55 |
govtSchemes: "Government Schemes",
|
| 56 |
viewSchemes: "View available schemes",
|
| 57 |
centralSchemes: "Central Schemes",
|
|
|
|
| 59 |
otherSchemes: "Other Schemes",
|
| 60 |
schemeDetails: "Scheme Details",
|
| 61 |
noSchemesAvailable: "No schemes available at the moment",
|
| 62 |
+
clearHistory: "Clear History",
|
| 63 |
+
confirmClearAll: "Are you sure you want to delete ALL your scan history? This cannot be undone.",
|
| 64 |
+
applyOnline: "Apply Online",
|
| 65 |
+
dailyTips: [
|
| 66 |
+
"Water your plants early in the morning to prevent fungal growth and ensure better absorption.",
|
| 67 |
+
"Use neem oil as a natural pesticide to protect your crops without harmful chemicals.",
|
| 68 |
+
"Rotating your crops helps maintain soil fertility and breaks the cycle of pests and diseases.",
|
| 69 |
+
"Ensuring proper spacing between plants improves air circulation and reduces moisture-built diseases.",
|
| 70 |
+
"Regularly inspect the underside of leaves; many pests hide there before causing visible damage.",
|
| 71 |
+
"Adding organic compost to your soil provides essential nutrients and improves its texture.",
|
| 72 |
+
"Mulching your soil helps retain moisture and suppresses weed growth effectively."
|
| 73 |
+
],
|
| 74 |
+
schemeLinks: {
|
| 75 |
+
s1: "https://pmkisan.gov.in/",
|
| 76 |
+
s2: "https://pmfby.gov.in/",
|
| 77 |
+
s3: "https://www.india.gov.in/spotlight/kisan-credit-card-kcc",
|
| 78 |
+
s4: "https://soilhealth.dac.gov.in/",
|
| 79 |
+
s5: "https://pmksy.gov.in/"
|
| 80 |
+
},
|
| 81 |
ourTeam: "OUR TEAM",
|
| 82 |
principal: "Principal: Dr. K. Baskaran, Principal, ACGCET",
|
| 83 |
principalInvestigator: "Principal Investigator (PI): Dr. S. Nageswari, ASP/EEE",
|
|
|
|
| 123 |
noHistory: "வரலாறு இல்லை",
|
| 124 |
confirmDelete: "நிச்சயமாக இதை நீக்க விரும்புகிறீர்களா?",
|
| 125 |
cancel: "ரத்து செய்",
|
| 126 |
+
clearHistory: "வரலாற்றை அழி",
|
| 127 |
+
confirmClearAll: "உங்கள் அனைத்து ஸ்கேன் வரலாற்றையும் நீக்க விரும்புகிறீர்களா? இதை மாற்ற முடியாது.",
|
| 128 |
+
applyOnline: "ஆன்லைனில் விண்ணப்பிக்கவும்",
|
| 129 |
+
dailyTips: [
|
| 130 |
+
"பூஞ்சை வளர்ச்சியைத் தடுக்கவும் சிறந்த உறிஞ்சுதலை உறுதி செய்யவும் காலையில் உங்கள் தாவரங்களுக்கு நீர் ஊற்றவும்.",
|
| 131 |
+
"வேப்ப எண்ணெயை இயற்கை பூச்சிக்கொல்லியாகப் பயன்படுத்துவதன் மூலம் பயிர்களைப் பாதுகாக்கலாம்.",
|
| 132 |
+
"பயிர் சுழற்சி முறையைப் பின்பற்றுவது மண்ணின் வளத்தை பராமரிக்கவும் பூச்சிகளைக் குறைக்கவும் உதவுகிறது.",
|
| 133 |
+
"செடிகளுக்கு இடையே போதிய இடைவெளி விடுவது காற்றோட்டத்தை மேம்படுத்தி நோய்களைக் குறைக்கிறது.",
|
| 134 |
+
"இலைகளின் அடியில் அவ்வப்போது பரிசோதிக்கவும்; பல பூச்சிகள் அங்கேயே மறைந்திருக்கும்.",
|
| 135 |
+
"மண்ணில் இயற்கை உரங்களைச் சேர்ப்பது அத்தியாவசிய ஊட்டச்சத்துக்களை வழங்குகிறது.",
|
| 136 |
+
"மண்ணில் மூடாக்கு (Mulching) இடுவது ஈரப்பதத்தை தக்கவைக்கவும் களைகளைக் குறைக்கவும் உதவுகிறது."
|
| 137 |
+
],
|
| 138 |
+
schemeLinks: {
|
| 139 |
+
s1: "https://pmkisan.gov.in/",
|
| 140 |
+
s2: "https://pmfby.gov.in/",
|
| 141 |
+
s3: "https://www.india.gov.in/spotlight/kisan-credit-card-kcc",
|
| 142 |
+
s4: "https://soilhealth.dac.gov.in/",
|
| 143 |
+
s5: "https://pmksy.gov.in/"
|
| 144 |
+
},
|
|
|
|
|
|
|
|
|
|
| 145 |
ourTeam: "எங்கள் குழு",
|
| 146 |
principal: "முதல்வர்: Dr. K. பாஸ்கரன், முதல்வர், ACGCET",
|
| 147 |
principalInvestigator: "முதன்மை ஆய்வாளர் (PI): Dr. S. நாகேஸ்வரி, ASP/EEE",
|
screens/AboutScreen.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
import React from 'react';
|
| 2 |
import { StyleSheet, Text, View, Image, ScrollView, TouchableOpacity, SafeAreaView, StatusBar, Dimensions } from 'react-native';
|
| 3 |
-
import { COLORS, SIZES } from '../constants/theme';
|
| 4 |
import { TRANSLATIONS } from '../constants/translations';
|
| 5 |
|
| 6 |
const { width } = Dimensions.get('window');
|
|
@@ -9,6 +9,19 @@ const AboutScreen = ({ route, navigation }) => {
|
|
| 9 |
const language = route.params?.language || 'en';
|
| 10 |
const t = TRANSLATIONS[language] || TRANSLATIONS['en'];
|
| 11 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
return (
|
| 13 |
<SafeAreaView style={styles.container}>
|
| 14 |
<StatusBar backgroundColor={COLORS.white} barStyle="dark-content" />
|
|
@@ -24,106 +37,85 @@ const AboutScreen = ({ route, navigation }) => {
|
|
| 24 |
|
| 25 |
<ScrollView contentContainerStyle={styles.scrollContent} showsVerticalScrollIndicator={false}>
|
| 26 |
|
| 27 |
-
{/* 1. Logos Section */}
|
| 28 |
-
<View style={styles.
|
| 29 |
-
<View style={styles.
|
| 30 |
-
<
|
| 31 |
-
source={require('../assets/uba_logo.png')}
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
<
|
| 39 |
-
source={require('../assets/logo.png')}
|
| 40 |
-
style={styles.mainLogo}
|
| 41 |
-
resizeMode="contain"
|
| 42 |
-
/>
|
| 43 |
-
</View>
|
| 44 |
-
|
| 45 |
-
<View style={styles.sideLogoContainer}>
|
| 46 |
-
<Image
|
| 47 |
-
source={require('../assets/act_logo.png')}
|
| 48 |
-
style={styles.sideLogo}
|
| 49 |
-
resizeMode="contain"
|
| 50 |
-
/>
|
| 51 |
</View>
|
|
|
|
| 52 |
</View>
|
| 53 |
|
| 54 |
-
{/* 2.
|
| 55 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
{t.
|
| 60 |
-
|
|
|
|
| 61 |
|
| 62 |
-
|
| 63 |
-
<View style={styles.sectionContainer}>
|
| 64 |
-
<View style={styles.sectionHeader}>
|
| 65 |
-
<Text style={styles.sectionIcon}>👁️</Text>
|
| 66 |
-
<Text style={styles.sectionTitle}>{t.visionTitle}</Text>
|
| 67 |
-
</View>
|
| 68 |
-
<Text style={styles.bodyText}>
|
| 69 |
-
{t.visionContent}
|
| 70 |
-
</Text>
|
| 71 |
-
</View>
|
| 72 |
-
|
| 73 |
-
<View style={styles.sectionContainer}>
|
| 74 |
-
<View style={styles.sectionHeader}>
|
| 75 |
-
<Text style={styles.sectionIcon}>🎯</Text>
|
| 76 |
-
<Text style={styles.sectionTitle}>{t.missionTitle}</Text>
|
| 77 |
-
</View>
|
| 78 |
<View style={styles.bulletList}>
|
| 79 |
-
|
| 80 |
-
<
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
<Text style={styles.bodyText}>{t.missionPoint2}</Text>
|
| 86 |
-
</View>
|
| 87 |
-
<View style={styles.bulletItem}>
|
| 88 |
-
<Text style={styles.bullet}>•</Text>
|
| 89 |
-
<Text style={styles.bodyText}>{t.missionPoint3}</Text>
|
| 90 |
-
</View>
|
| 91 |
</View>
|
| 92 |
-
</
|
| 93 |
|
| 94 |
-
{/*
|
| 95 |
-
<View style={styles.
|
| 96 |
-
<View style={styles.
|
| 97 |
-
<
|
| 98 |
-
|
|
|
|
|
|
|
| 99 |
</View>
|
| 100 |
|
| 101 |
-
<View style={styles.
|
| 102 |
-
<
|
| 103 |
-
|
| 104 |
-
|
|
|
|
| 105 |
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
|
|
|
|
|
|
|
|
|
| 122 |
</View>
|
| 123 |
</View>
|
| 124 |
</View>
|
| 125 |
|
| 126 |
-
<View style={{ height:
|
| 127 |
</ScrollView>
|
| 128 |
</SafeAreaView>
|
| 129 |
);
|
|
@@ -132,143 +124,179 @@ const AboutScreen = ({ route, navigation }) => {
|
|
| 132 |
const styles = StyleSheet.create({
|
| 133 |
container: {
|
| 134 |
flex: 1,
|
| 135 |
-
backgroundColor:
|
| 136 |
},
|
| 137 |
header: {
|
| 138 |
flexDirection: 'row',
|
| 139 |
alignItems: 'center',
|
| 140 |
justifyContent: 'space-between',
|
| 141 |
-
paddingHorizontal:
|
| 142 |
-
|
| 143 |
-
|
|
|
|
| 144 |
},
|
| 145 |
backButton: {
|
| 146 |
-
padding:
|
| 147 |
},
|
| 148 |
backButtonText: {
|
| 149 |
-
fontSize:
|
| 150 |
-
color:
|
| 151 |
-
fontWeight: '
|
| 152 |
},
|
| 153 |
headerTitle: {
|
| 154 |
-
fontSize:
|
| 155 |
-
fontWeight: '
|
| 156 |
-
color:
|
| 157 |
-
letterSpacing: 1,
|
| 158 |
-
textTransform: 'uppercase',
|
| 159 |
},
|
| 160 |
scrollContent: {
|
| 161 |
-
|
| 162 |
-
paddingTop: 10,
|
| 163 |
},
|
| 164 |
-
|
| 165 |
-
flexDirection: 'row',
|
| 166 |
alignItems: 'center',
|
| 167 |
-
justifyContent: 'space-between',
|
| 168 |
marginBottom: 30,
|
| 169 |
-
marginTop: 10,
|
| 170 |
},
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
height: 70,
|
| 174 |
-
justifyContent: 'center',
|
| 175 |
alignItems: 'center',
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
width: '100%',
|
| 180 |
-
height: '100%',
|
| 181 |
},
|
| 182 |
-
|
| 183 |
-
width:
|
| 184 |
-
height:
|
|
|
|
|
|
|
| 185 |
justifyContent: 'center',
|
| 186 |
alignItems: 'center',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 187 |
},
|
| 188 |
mainLogo: {
|
| 189 |
-
width: '
|
| 190 |
-
height: '
|
| 191 |
},
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
color: '#1B5E20', // Dark Green
|
| 196 |
-
textAlign: 'center',
|
| 197 |
-
marginBottom: 20,
|
| 198 |
-
letterSpacing: 0.5,
|
| 199 |
-
textTransform: 'uppercase',
|
| 200 |
},
|
| 201 |
-
|
| 202 |
-
fontSize:
|
| 203 |
-
|
| 204 |
-
color:
|
| 205 |
-
|
| 206 |
-
marginBottom: 20,
|
| 207 |
-
fontWeight: '400',
|
| 208 |
},
|
| 209 |
-
|
|
|
|
|
|
|
|
|
|
| 210 |
marginBottom: 20,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 211 |
},
|
| 212 |
-
|
| 213 |
flexDirection: 'row',
|
| 214 |
alignItems: 'center',
|
| 215 |
-
marginBottom:
|
| 216 |
},
|
| 217 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 218 |
fontSize: 20,
|
| 219 |
-
marginRight: 10,
|
| 220 |
-
color: '#2E7D32',
|
| 221 |
},
|
| 222 |
-
|
| 223 |
-
fontSize:
|
| 224 |
-
fontWeight: '
|
| 225 |
-
color:
|
| 226 |
-
|
| 227 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 228 |
},
|
| 229 |
bulletList: {
|
| 230 |
marginTop: 5,
|
| 231 |
},
|
| 232 |
bulletItem: {
|
| 233 |
flexDirection: 'row',
|
|
|
|
| 234 |
marginBottom: 8,
|
| 235 |
-
alignItems: 'flex-start',
|
| 236 |
},
|
| 237 |
-
|
| 238 |
-
|
|
|
|
|
|
|
|
|
|
| 239 |
marginRight: 10,
|
| 240 |
-
color: '#2E7D32',
|
| 241 |
-
lineHeight: 24,
|
| 242 |
},
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 248 |
},
|
| 249 |
teamMember: {
|
| 250 |
-
|
|
|
|
|
|
|
| 251 |
},
|
| 252 |
roleLabel: {
|
| 253 |
-
fontSize:
|
| 254 |
-
|
| 255 |
-
|
| 256 |
marginBottom: 2,
|
| 257 |
},
|
| 258 |
memberName: {
|
| 259 |
fontSize: 15,
|
| 260 |
-
|
| 261 |
-
|
| 262 |
},
|
| 263 |
-
|
| 264 |
marginTop: 5,
|
| 265 |
-
paddingLeft: 10,
|
| 266 |
},
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
|
| 271 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 272 |
}
|
| 273 |
});
|
| 274 |
|
|
|
|
| 1 |
import React from 'react';
|
| 2 |
import { StyleSheet, Text, View, Image, ScrollView, TouchableOpacity, SafeAreaView, StatusBar, Dimensions } from 'react-native';
|
| 3 |
+
import { COLORS, SIZES, FONTS } from '../constants/theme';
|
| 4 |
import { TRANSLATIONS } from '../constants/translations';
|
| 5 |
|
| 6 |
const { width } = Dimensions.get('window');
|
|
|
|
| 9 |
const language = route.params?.language || 'en';
|
| 10 |
const t = TRANSLATIONS[language] || TRANSLATIONS['en'];
|
| 11 |
|
| 12 |
+
const InfoCard = ({ icon, title, content, children }) => (
|
| 13 |
+
<View style={styles.card}>
|
| 14 |
+
<View style={styles.cardHeader}>
|
| 15 |
+
<View style={styles.cardIconCircle}>
|
| 16 |
+
<Text style={styles.cardIcon}>{icon}</Text>
|
| 17 |
+
</View>
|
| 18 |
+
<Text style={styles.cardTitle}>{title}</Text>
|
| 19 |
+
</View>
|
| 20 |
+
{content ? <Text style={styles.cardBody}>{content}</Text> : null}
|
| 21 |
+
{children}
|
| 22 |
+
</View>
|
| 23 |
+
);
|
| 24 |
+
|
| 25 |
return (
|
| 26 |
<SafeAreaView style={styles.container}>
|
| 27 |
<StatusBar backgroundColor={COLORS.white} barStyle="dark-content" />
|
|
|
|
| 37 |
|
| 38 |
<ScrollView contentContainerStyle={styles.scrollContent} showsVerticalScrollIndicator={false}>
|
| 39 |
|
| 40 |
+
{/* 1. Logos Section - Grouped Header */}
|
| 41 |
+
<View style={styles.brandHeader}>
|
| 42 |
+
<View style={styles.logoGrid}>
|
| 43 |
+
<View style={styles.brandBox}>
|
| 44 |
+
<Image source={require('../assets/uba_logo.png')} style={styles.sideLogo} resizeMode="contain" />
|
| 45 |
+
</View>
|
| 46 |
+
<View style={[styles.brandBox, styles.mainBrandBox]}>
|
| 47 |
+
<Image source={require('../assets/logo.png')} style={styles.mainLogo} resizeMode="contain" />
|
| 48 |
+
</View>
|
| 49 |
+
<View style={styles.brandBox}>
|
| 50 |
+
<Image source={require('../assets/act_logo.png')} style={styles.sideLogo} resizeMode="contain" />
|
| 51 |
+
</View>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
</View>
|
| 53 |
+
<Text style={styles.pageHeaderTitle}>{t.aboutTitle}</Text>
|
| 54 |
</View>
|
| 55 |
|
| 56 |
+
{/* 2. Content Cards */}
|
| 57 |
+
<InfoCard
|
| 58 |
+
icon="🌱"
|
| 59 |
+
title={t.aboutApp}
|
| 60 |
+
content={t.aboutContent}
|
| 61 |
+
/>
|
| 62 |
|
| 63 |
+
<InfoCard
|
| 64 |
+
icon="👁️"
|
| 65 |
+
title={t.visionTitle}
|
| 66 |
+
content={t.visionContent}
|
| 67 |
+
/>
|
| 68 |
|
| 69 |
+
<InfoCard icon="🎯" title={t.missionTitle}>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
<View style={styles.bulletList}>
|
| 71 |
+
{[t.missionPoint1, t.missionPoint2, t.missionPoint3].map((point, i) => (
|
| 72 |
+
<View key={i} style={styles.bulletItem}>
|
| 73 |
+
<View style={styles.bulletPoint} />
|
| 74 |
+
<Text style={styles.bulletText}>{point}</Text>
|
| 75 |
+
</View>
|
| 76 |
+
))}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
</View>
|
| 78 |
+
</InfoCard>
|
| 79 |
|
| 80 |
+
{/* 3. Team Card */}
|
| 81 |
+
<View style={[styles.card, styles.teamCard]}>
|
| 82 |
+
<View style={styles.cardHeader}>
|
| 83 |
+
<View style={[styles.cardIconCircle, { backgroundColor: '#E3F2FD' }]}>
|
| 84 |
+
<Text style={styles.cardIcon}>👥</Text>
|
| 85 |
+
</View>
|
| 86 |
+
<Text style={styles.cardTitle}>{t.ourTeam}</Text>
|
| 87 |
</View>
|
| 88 |
|
| 89 |
+
<View style={styles.teamGrid}>
|
| 90 |
+
<View style={styles.teamMember}>
|
| 91 |
+
<Text style={styles.roleLabel}>{t.principal?.split(':')[0] || 'Principal'}</Text>
|
| 92 |
+
<Text style={styles.memberName}>{t.principal?.split(':')[1] || ''}</Text>
|
| 93 |
+
</View>
|
| 94 |
|
| 95 |
+
<View style={styles.teamMember}>
|
| 96 |
+
<Text style={styles.roleLabel}>{t.principalInvestigator?.split(':')[0] || 'PI'}</Text>
|
| 97 |
+
<Text style={styles.memberName}>{t.principalInvestigator?.split(':')[1] || ''}</Text>
|
| 98 |
+
</View>
|
| 99 |
|
| 100 |
+
<View style={styles.teamMember}>
|
| 101 |
+
<Text style={styles.roleLabel}>{t.coPi?.split(':')[0] || 'Co-PI'}</Text>
|
| 102 |
+
<Text style={styles.memberName}>{t.coPi?.split(':')[1] || ''}</Text>
|
| 103 |
+
</View>
|
| 104 |
|
| 105 |
+
<View style={styles.studentSection}>
|
| 106 |
+
<Text style={styles.roleLabel}>{t.studentInnovators}</Text>
|
| 107 |
+
<View style={styles.studentChips}>
|
| 108 |
+
{t.studentsList?.map((student, i) => (
|
| 109 |
+
<View key={i} style={styles.chip}>
|
| 110 |
+
<Text style={styles.chipText}>{student}</Text>
|
| 111 |
+
</View>
|
| 112 |
+
))}
|
| 113 |
+
</View>
|
| 114 |
</View>
|
| 115 |
</View>
|
| 116 |
</View>
|
| 117 |
|
| 118 |
+
<View style={{ height: 40 }} />
|
| 119 |
</ScrollView>
|
| 120 |
</SafeAreaView>
|
| 121 |
);
|
|
|
|
| 124 |
const styles = StyleSheet.create({
|
| 125 |
container: {
|
| 126 |
flex: 1,
|
| 127 |
+
backgroundColor: '#F8F9FA',
|
| 128 |
},
|
| 129 |
header: {
|
| 130 |
flexDirection: 'row',
|
| 131 |
alignItems: 'center',
|
| 132 |
justifyContent: 'space-between',
|
| 133 |
+
paddingHorizontal: 15,
|
| 134 |
+
paddingTop: 10,
|
| 135 |
+
height: 60,
|
| 136 |
+
backgroundColor: '#fff',
|
| 137 |
},
|
| 138 |
backButton: {
|
| 139 |
+
padding: 10,
|
| 140 |
},
|
| 141 |
backButtonText: {
|
| 142 |
+
fontSize: 24,
|
| 143 |
+
color: COLORS.primary,
|
| 144 |
+
fontWeight: 'bold',
|
| 145 |
},
|
| 146 |
headerTitle: {
|
| 147 |
+
fontSize: 18,
|
| 148 |
+
fontWeight: 'bold',
|
| 149 |
+
color: COLORS.primary,
|
|
|
|
|
|
|
| 150 |
},
|
| 151 |
scrollContent: {
|
| 152 |
+
padding: 20,
|
|
|
|
| 153 |
},
|
| 154 |
+
brandHeader: {
|
|
|
|
| 155 |
alignItems: 'center',
|
|
|
|
| 156 |
marginBottom: 30,
|
|
|
|
| 157 |
},
|
| 158 |
+
logoGrid: {
|
| 159 |
+
flexDirection: 'row',
|
|
|
|
|
|
|
| 160 |
alignItems: 'center',
|
| 161 |
+
justifyContent: 'center',
|
| 162 |
+
gap: 15,
|
| 163 |
+
marginBottom: 15,
|
|
|
|
|
|
|
| 164 |
},
|
| 165 |
+
brandBox: {
|
| 166 |
+
width: 60,
|
| 167 |
+
height: 60,
|
| 168 |
+
backgroundColor: '#fff',
|
| 169 |
+
borderRadius: 12,
|
| 170 |
justifyContent: 'center',
|
| 171 |
alignItems: 'center',
|
| 172 |
+
elevation: 2,
|
| 173 |
+
shadowColor: '#000',
|
| 174 |
+
shadowOffset: { width: 0, height: 2 },
|
| 175 |
+
shadowOpacity: 0.1,
|
| 176 |
+
shadowRadius: 4,
|
| 177 |
+
},
|
| 178 |
+
mainBrandBox: {
|
| 179 |
+
width: 80,
|
| 180 |
+
height: 80,
|
| 181 |
+
borderRadius: 20,
|
| 182 |
+
borderWidth: 1,
|
| 183 |
+
borderColor: COLORS.primary + '30',
|
| 184 |
},
|
| 185 |
mainLogo: {
|
| 186 |
+
width: '80%',
|
| 187 |
+
height: '80%',
|
| 188 |
},
|
| 189 |
+
sideLogo: {
|
| 190 |
+
width: '70%',
|
| 191 |
+
height: '70%',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 192 |
},
|
| 193 |
+
pageHeaderTitle: {
|
| 194 |
+
fontSize: 22,
|
| 195 |
+
fontWeight: 'bold',
|
| 196 |
+
color: COLORS.primary,
|
| 197 |
+
letterSpacing: 1,
|
|
|
|
|
|
|
| 198 |
},
|
| 199 |
+
card: {
|
| 200 |
+
backgroundColor: '#fff',
|
| 201 |
+
borderRadius: 20,
|
| 202 |
+
padding: 20,
|
| 203 |
marginBottom: 20,
|
| 204 |
+
elevation: 3,
|
| 205 |
+
shadowColor: '#000',
|
| 206 |
+
shadowOffset: { width: 0, height: 2 },
|
| 207 |
+
shadowOpacity: 0.1,
|
| 208 |
+
shadowRadius: 8,
|
| 209 |
},
|
| 210 |
+
cardHeader: {
|
| 211 |
flexDirection: 'row',
|
| 212 |
alignItems: 'center',
|
| 213 |
+
marginBottom: 15,
|
| 214 |
},
|
| 215 |
+
cardIconCircle: {
|
| 216 |
+
width: 40,
|
| 217 |
+
height: 40,
|
| 218 |
+
borderRadius: 20,
|
| 219 |
+
backgroundColor: '#F1F8E9',
|
| 220 |
+
justifyContent: 'center',
|
| 221 |
+
alignItems: 'center',
|
| 222 |
+
marginRight: 12,
|
| 223 |
+
},
|
| 224 |
+
cardIcon: {
|
| 225 |
fontSize: 20,
|
|
|
|
|
|
|
| 226 |
},
|
| 227 |
+
cardTitle: {
|
| 228 |
+
fontSize: 16,
|
| 229 |
+
fontWeight: 'bold',
|
| 230 |
+
color: COLORS.primary,
|
| 231 |
+
},
|
| 232 |
+
cardBody: {
|
| 233 |
+
fontSize: 14,
|
| 234 |
+
lineHeight: 22,
|
| 235 |
+
color: '#444',
|
| 236 |
+
textAlign: 'justify',
|
| 237 |
},
|
| 238 |
bulletList: {
|
| 239 |
marginTop: 5,
|
| 240 |
},
|
| 241 |
bulletItem: {
|
| 242 |
flexDirection: 'row',
|
| 243 |
+
alignItems: 'center',
|
| 244 |
marginBottom: 8,
|
|
|
|
| 245 |
},
|
| 246 |
+
bulletPoint: {
|
| 247 |
+
width: 6,
|
| 248 |
+
height: 6,
|
| 249 |
+
borderRadius: 3,
|
| 250 |
+
backgroundColor: COLORS.primary,
|
| 251 |
marginRight: 10,
|
|
|
|
|
|
|
| 252 |
},
|
| 253 |
+
bulletText: {
|
| 254 |
+
fontSize: 14,
|
| 255 |
+
color: '#444',
|
| 256 |
+
},
|
| 257 |
+
teamCard: {
|
| 258 |
+
backgroundColor: '#fff',
|
| 259 |
+
},
|
| 260 |
+
teamGrid: {
|
| 261 |
+
gap: 15,
|
| 262 |
},
|
| 263 |
teamMember: {
|
| 264 |
+
paddingBottom: 10,
|
| 265 |
+
borderBottomWidth: 1,
|
| 266 |
+
borderBottomColor: '#f0f0f0',
|
| 267 |
},
|
| 268 |
roleLabel: {
|
| 269 |
+
fontSize: 12,
|
| 270 |
+
color: COLORS.gray,
|
| 271 |
+
fontWeight: 'bold',
|
| 272 |
marginBottom: 2,
|
| 273 |
},
|
| 274 |
memberName: {
|
| 275 |
fontSize: 15,
|
| 276 |
+
color: COLORS.text,
|
| 277 |
+
fontWeight: '600',
|
| 278 |
},
|
| 279 |
+
studentSection: {
|
| 280 |
marginTop: 5,
|
|
|
|
| 281 |
},
|
| 282 |
+
studentChips: {
|
| 283 |
+
flexDirection: 'row',
|
| 284 |
+
flexWrap: 'wrap',
|
| 285 |
+
gap: 8,
|
| 286 |
+
marginTop: 10,
|
| 287 |
+
},
|
| 288 |
+
chip: {
|
| 289 |
+
backgroundColor: '#F5F5F5',
|
| 290 |
+
paddingHorizontal: 12,
|
| 291 |
+
paddingVertical: 6,
|
| 292 |
+
borderRadius: 15,
|
| 293 |
+
borderWidth: 1,
|
| 294 |
+
borderColor: '#E0E0E0',
|
| 295 |
+
},
|
| 296 |
+
chipText: {
|
| 297 |
+
fontSize: 13,
|
| 298 |
+
color: '#666',
|
| 299 |
+
fontWeight: '500',
|
| 300 |
}
|
| 301 |
});
|
| 302 |
|
screens/HistoryScreen.js
CHANGED
|
@@ -23,12 +23,12 @@ const HistoryScreen = ({ navigation, route }) => {
|
|
| 23 |
|
| 24 |
const handleDelete = async (item) => {
|
| 25 |
Alert.alert(
|
| 26 |
-
|
| 27 |
-
|
| 28 |
[
|
| 29 |
{ text: t.cancel, style: "cancel" },
|
| 30 |
{
|
| 31 |
-
text:
|
| 32 |
style: "destructive",
|
| 33 |
onPress: async () => {
|
| 34 |
await deleteScan(item.timestamp);
|
|
@@ -47,18 +47,24 @@ const HistoryScreen = ({ navigation, route }) => {
|
|
| 47 |
analysisResult: item.analysisResult,
|
| 48 |
language: language
|
| 49 |
})}
|
|
|
|
| 50 |
>
|
| 51 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
<View style={styles.cardContent}>
|
| 53 |
-
<Text style={styles.cropName}>{item.analysisResult.crop}</Text>
|
| 54 |
<Text style={styles.diseaseName}>{item.analysisResult.name[language]}</Text>
|
| 55 |
-
<Text style={styles.
|
| 56 |
</View>
|
| 57 |
<TouchableOpacity
|
| 58 |
-
style={styles.
|
| 59 |
onPress={() => handleDelete(item)}
|
| 60 |
>
|
| 61 |
-
<Text style={styles.
|
| 62 |
</TouchableOpacity>
|
| 63 |
</TouchableOpacity>
|
| 64 |
);
|
|
@@ -66,16 +72,36 @@ const HistoryScreen = ({ navigation, route }) => {
|
|
| 66 |
return (
|
| 67 |
<View style={styles.container}>
|
| 68 |
<View style={styles.header}>
|
| 69 |
-
<
|
| 70 |
-
<
|
| 71 |
-
|
| 72 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
{history.length > 0 && (
|
| 74 |
-
<TouchableOpacity
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
</TouchableOpacity>
|
| 80 |
)}
|
| 81 |
</View>
|
|
@@ -83,11 +109,13 @@ const HistoryScreen = ({ navigation, route }) => {
|
|
| 83 |
<FlatList
|
| 84 |
data={history}
|
| 85 |
renderItem={renderItem}
|
| 86 |
-
keyExtractor={(item) => item.
|
| 87 |
-
contentContainerStyle={styles.
|
| 88 |
ListEmptyComponent={
|
| 89 |
-
<View style={styles.
|
| 90 |
-
<Text style={styles.
|
|
|
|
|
|
|
| 91 |
</View>
|
| 92 |
}
|
| 93 |
/>
|
|
@@ -101,87 +129,152 @@ const styles = StyleSheet.create({
|
|
| 101 |
backgroundColor: COLORS.background,
|
| 102 |
},
|
| 103 |
header: {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
flexDirection: 'row',
|
| 105 |
alignItems: 'center',
|
| 106 |
justifyContent: 'space-between',
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
paddingHorizontal: 20,
|
| 110 |
-
backgroundColor: COLORS.primary,
|
| 111 |
-
borderBottomLeftRadius: 30,
|
| 112 |
-
borderBottomRightRadius: 30,
|
| 113 |
},
|
| 114 |
-
|
| 115 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
},
|
| 117 |
-
|
| 118 |
fontSize: 24,
|
| 119 |
-
color:
|
|
|
|
| 120 |
},
|
| 121 |
headerTitle: {
|
| 122 |
fontSize: 20,
|
| 123 |
-
fontWeight: '
|
| 124 |
-
color:
|
|
|
|
| 125 |
},
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
},
|
| 130 |
-
|
| 131 |
-
padding:
|
| 132 |
},
|
| 133 |
card: {
|
| 134 |
flexDirection: 'row',
|
| 135 |
-
backgroundColor:
|
| 136 |
-
borderRadius:
|
| 137 |
marginBottom: 15,
|
| 138 |
-
padding:
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
|
|
|
|
|
|
| 144 |
},
|
| 145 |
thumbnail: {
|
| 146 |
-
width:
|
| 147 |
-
height:
|
| 148 |
-
borderRadius:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 149 |
},
|
| 150 |
cardContent: {
|
| 151 |
flex: 1,
|
| 152 |
marginLeft: 15,
|
| 153 |
-
justifyContent: 'center',
|
| 154 |
},
|
| 155 |
cropName: {
|
| 156 |
-
fontSize:
|
| 157 |
-
color: COLORS.
|
| 158 |
-
fontWeight: '
|
|
|
|
|
|
|
| 159 |
},
|
| 160 |
diseaseName: {
|
| 161 |
-
fontSize:
|
| 162 |
-
fontWeight: '
|
| 163 |
color: COLORS.text,
|
| 164 |
-
marginBottom:
|
| 165 |
},
|
| 166 |
-
|
| 167 |
fontSize: 12,
|
| 168 |
-
color: COLORS.
|
|
|
|
| 169 |
},
|
| 170 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 171 |
justifyContent: 'center',
|
| 172 |
-
|
| 173 |
},
|
| 174 |
-
|
| 175 |
-
fontSize:
|
|
|
|
| 176 |
},
|
| 177 |
-
|
| 178 |
flex: 1,
|
| 179 |
-
paddingTop:
|
| 180 |
alignItems: 'center',
|
|
|
|
| 181 |
},
|
| 182 |
-
|
| 183 |
-
fontSize:
|
| 184 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 185 |
}
|
| 186 |
});
|
| 187 |
|
|
|
|
| 23 |
|
| 24 |
const handleDelete = async (item) => {
|
| 25 |
Alert.alert(
|
| 26 |
+
language === 'ta' ? "ஸ்கேனை நீக்கவா?" : "Delete Scan?",
|
| 27 |
+
language === 'ta' ? "நிச்சயமாக இதை நீக்க விரும்புகிறீர்களா?" : "Are you sure you want to remove this from your history?",
|
| 28 |
[
|
| 29 |
{ text: t.cancel, style: "cancel" },
|
| 30 |
{
|
| 31 |
+
text: language === 'ta' ? "நீக்கு" : "Delete",
|
| 32 |
style: "destructive",
|
| 33 |
onPress: async () => {
|
| 34 |
await deleteScan(item.timestamp);
|
|
|
|
| 47 |
analysisResult: item.analysisResult,
|
| 48 |
language: language
|
| 49 |
})}
|
| 50 |
+
activeOpacity={0.7}
|
| 51 |
>
|
| 52 |
+
<View style={styles.thumbnailContainer}>
|
| 53 |
+
<Image source={{ uri: item.imageUri }} style={styles.thumbnail} />
|
| 54 |
+
<View style={styles.statusDotOverlay}>
|
| 55 |
+
<View style={[styles.miniDot, { backgroundColor: item.analysisResult.isHealthy ? COLORS.success : COLORS.error }]} />
|
| 56 |
+
</View>
|
| 57 |
+
</View>
|
| 58 |
<View style={styles.cardContent}>
|
| 59 |
+
<Text style={styles.cropName}>{item.analysisResult.crop.toUpperCase()}</Text>
|
| 60 |
<Text style={styles.diseaseName}>{item.analysisResult.name[language]}</Text>
|
| 61 |
+
<Text style={styles.dateText}>{new Date(item.timestamp).toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' })}</Text>
|
| 62 |
</View>
|
| 63 |
<TouchableOpacity
|
| 64 |
+
style={styles.deleteIconButton}
|
| 65 |
onPress={() => handleDelete(item)}
|
| 66 |
>
|
| 67 |
+
<Text style={styles.trashIcon}>🗑️</Text>
|
| 68 |
</TouchableOpacity>
|
| 69 |
</TouchableOpacity>
|
| 70 |
);
|
|
|
|
| 72 |
return (
|
| 73 |
<View style={styles.container}>
|
| 74 |
<View style={styles.header}>
|
| 75 |
+
<View style={styles.headerTop}>
|
| 76 |
+
<TouchableOpacity onPress={() => navigation.goBack()} style={styles.backBtn}>
|
| 77 |
+
<Text style={styles.backBtnText}>←</Text>
|
| 78 |
+
</TouchableOpacity>
|
| 79 |
+
<Text style={styles.headerTitle}>{t.history}</Text>
|
| 80 |
+
<View style={{ width: 44 }} />
|
| 81 |
+
</View>
|
| 82 |
+
|
| 83 |
{history.length > 0 && (
|
| 84 |
+
<TouchableOpacity
|
| 85 |
+
style={styles.clearBadge}
|
| 86 |
+
onPress={() => {
|
| 87 |
+
Alert.alert(
|
| 88 |
+
t.clearHistory,
|
| 89 |
+
t.confirmClearAll,
|
| 90 |
+
[
|
| 91 |
+
{ text: t.cancel, style: "cancel" },
|
| 92 |
+
{
|
| 93 |
+
text: t.clearHistory,
|
| 94 |
+
style: "destructive",
|
| 95 |
+
onPress: async () => {
|
| 96 |
+
await clearHistory();
|
| 97 |
+
loadHistory();
|
| 98 |
+
}
|
| 99 |
+
}
|
| 100 |
+
]
|
| 101 |
+
);
|
| 102 |
+
}}
|
| 103 |
+
>
|
| 104 |
+
<Text style={styles.clearBadgeText}>{t.clearHistory.toUpperCase()}</Text>
|
| 105 |
</TouchableOpacity>
|
| 106 |
)}
|
| 107 |
</View>
|
|
|
|
| 109 |
<FlatList
|
| 110 |
data={history}
|
| 111 |
renderItem={renderItem}
|
| 112 |
+
keyExtractor={(item) => item.timestamp.toString()}
|
| 113 |
+
contentContainerStyle={styles.listContainer}
|
| 114 |
ListEmptyComponent={
|
| 115 |
+
<View style={styles.emptyBox}>
|
| 116 |
+
<Text style={styles.emptyIcon}>📂</Text>
|
| 117 |
+
<Text style={styles.emptyTitle}>{t.noHistory}</Text>
|
| 118 |
+
<Text style={styles.emptySub}>{language === 'ta' ? "நீங்கள் செய்த ஸ்கேன்கள் இங்கே தோன்றும்" : "Your scan history will appear here once you start scanning leaves."}</Text>
|
| 119 |
</View>
|
| 120 |
}
|
| 121 |
/>
|
|
|
|
| 129 |
backgroundColor: COLORS.background,
|
| 130 |
},
|
| 131 |
header: {
|
| 132 |
+
backgroundColor: COLORS.primary,
|
| 133 |
+
paddingTop: 50,
|
| 134 |
+
paddingBottom: 25,
|
| 135 |
+
paddingHorizontal: SIZES.padding,
|
| 136 |
+
borderBottomLeftRadius: 35,
|
| 137 |
+
borderBottomRightRadius: 35,
|
| 138 |
+
...COLORS.shadow.lg,
|
| 139 |
+
alignItems: 'center',
|
| 140 |
+
},
|
| 141 |
+
headerTop: {
|
| 142 |
flexDirection: 'row',
|
| 143 |
alignItems: 'center',
|
| 144 |
justifyContent: 'space-between',
|
| 145 |
+
width: '100%',
|
| 146 |
+
marginBottom: 15,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
},
|
| 148 |
+
backBtn: {
|
| 149 |
+
width: 44,
|
| 150 |
+
height: 44,
|
| 151 |
+
borderRadius: 22,
|
| 152 |
+
backgroundColor: 'rgba(255,255,255,0.2)',
|
| 153 |
+
justifyContent: 'center',
|
| 154 |
+
alignItems: 'center',
|
| 155 |
},
|
| 156 |
+
backBtnText: {
|
| 157 |
fontSize: 24,
|
| 158 |
+
color: COLORS.white,
|
| 159 |
+
fontWeight: 'bold',
|
| 160 |
},
|
| 161 |
headerTitle: {
|
| 162 |
fontSize: 20,
|
| 163 |
+
fontWeight: '900',
|
| 164 |
+
color: COLORS.white,
|
| 165 |
+
letterSpacing: 0.5,
|
| 166 |
},
|
| 167 |
+
clearBadge: {
|
| 168 |
+
backgroundColor: 'rgba(0,0,0,0.2)',
|
| 169 |
+
paddingHorizontal: 12,
|
| 170 |
+
paddingVertical: 6,
|
| 171 |
+
borderRadius: 10,
|
| 172 |
+
borderWidth: 1,
|
| 173 |
+
borderColor: 'rgba(255,255,255,0.1)',
|
| 174 |
+
},
|
| 175 |
+
clearBadgeText: {
|
| 176 |
+
color: 'rgba(255,255,255,0.8)',
|
| 177 |
+
fontSize: 10,
|
| 178 |
+
fontWeight: '900',
|
| 179 |
+
letterSpacing: 1,
|
| 180 |
},
|
| 181 |
+
listContainer: {
|
| 182 |
+
padding: SIZES.padding,
|
| 183 |
},
|
| 184 |
card: {
|
| 185 |
flexDirection: 'row',
|
| 186 |
+
backgroundColor: COLORS.surface,
|
| 187 |
+
borderRadius: 22,
|
| 188 |
marginBottom: 15,
|
| 189 |
+
padding: 12,
|
| 190 |
+
alignItems: 'center',
|
| 191 |
+
borderWidth: 1,
|
| 192 |
+
borderColor: COLORS.border,
|
| 193 |
+
...COLORS.shadow.sm,
|
| 194 |
+
},
|
| 195 |
+
thumbnailContainer: {
|
| 196 |
+
position: 'relative',
|
| 197 |
},
|
| 198 |
thumbnail: {
|
| 199 |
+
width: 64,
|
| 200 |
+
height: 64,
|
| 201 |
+
borderRadius: 15,
|
| 202 |
+
backgroundColor: COLORS.background,
|
| 203 |
+
},
|
| 204 |
+
statusDotOverlay: {
|
| 205 |
+
position: 'absolute',
|
| 206 |
+
bottom: -2,
|
| 207 |
+
right: -2,
|
| 208 |
+
backgroundColor: COLORS.white,
|
| 209 |
+
width: 14,
|
| 210 |
+
height: 14,
|
| 211 |
+
borderRadius: 7,
|
| 212 |
+
justifyContent: 'center',
|
| 213 |
+
alignItems: 'center',
|
| 214 |
+
...COLORS.shadow.sm,
|
| 215 |
+
},
|
| 216 |
+
miniDot: {
|
| 217 |
+
width: 8,
|
| 218 |
+
height: 8,
|
| 219 |
+
borderRadius: 4,
|
| 220 |
},
|
| 221 |
cardContent: {
|
| 222 |
flex: 1,
|
| 223 |
marginLeft: 15,
|
|
|
|
| 224 |
},
|
| 225 |
cropName: {
|
| 226 |
+
fontSize: 10,
|
| 227 |
+
color: COLORS.textLight,
|
| 228 |
+
fontWeight: '900',
|
| 229 |
+
letterSpacing: 1,
|
| 230 |
+
marginBottom: 2,
|
| 231 |
},
|
| 232 |
diseaseName: {
|
| 233 |
+
fontSize: 17,
|
| 234 |
+
fontWeight: '800',
|
| 235 |
color: COLORS.text,
|
| 236 |
+
marginBottom: 4,
|
| 237 |
},
|
| 238 |
+
dateText: {
|
| 239 |
fontSize: 12,
|
| 240 |
+
color: COLORS.textLight,
|
| 241 |
+
fontWeight: '600',
|
| 242 |
},
|
| 243 |
+
deleteIconButton: {
|
| 244 |
+
width: 40,
|
| 245 |
+
height: 40,
|
| 246 |
+
borderRadius: 20,
|
| 247 |
+
backgroundColor: COLORS.background,
|
| 248 |
justifyContent: 'center',
|
| 249 |
+
alignItems: 'center',
|
| 250 |
},
|
| 251 |
+
trashIcon: {
|
| 252 |
+
fontSize: 18,
|
| 253 |
+
opacity: 0.6,
|
| 254 |
},
|
| 255 |
+
emptyBox: {
|
| 256 |
flex: 1,
|
| 257 |
+
paddingTop: 80,
|
| 258 |
alignItems: 'center',
|
| 259 |
+
paddingHorizontal: 40,
|
| 260 |
},
|
| 261 |
+
emptyIcon: {
|
| 262 |
+
fontSize: 64,
|
| 263 |
+
marginBottom: 20,
|
| 264 |
+
opacity: 0.2,
|
| 265 |
+
},
|
| 266 |
+
emptyTitle: {
|
| 267 |
+
fontSize: 18,
|
| 268 |
+
fontWeight: '900',
|
| 269 |
+
color: COLORS.text,
|
| 270 |
+
marginBottom: 8,
|
| 271 |
+
},
|
| 272 |
+
emptySub: {
|
| 273 |
+
fontSize: 14,
|
| 274 |
+
color: COLORS.textLight,
|
| 275 |
+
textAlign: 'center',
|
| 276 |
+
lineHeight: 20,
|
| 277 |
+
fontWeight: '500',
|
| 278 |
}
|
| 279 |
});
|
| 280 |
|
screens/HomeScreen.js
CHANGED
|
@@ -10,24 +10,27 @@ import { getHistory } from '../utils/storage';
|
|
| 10 |
const { width } = Dimensions.get('window');
|
| 11 |
|
| 12 |
const HomeScreen = ({ route, navigation }) => {
|
| 13 |
-
// Robust fallback: Check if route.params exists, then check language, otherwise default to 'en'
|
| 14 |
const language = route.params?.language || 'en';
|
| 15 |
const [recentScans, setRecentScans] = useState([]);
|
| 16 |
const [stats, setStats] = useState({ total: 0, healthy: 0, issues: 0 });
|
|
|
|
| 17 |
|
| 18 |
-
// Safety check for translations
|
| 19 |
const translations = TRANSLATIONS[language] || TRANSLATIONS['en'];
|
| 20 |
const t = translations;
|
| 21 |
|
| 22 |
useFocusEffect(
|
| 23 |
useCallback(() => {
|
| 24 |
loadData();
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
}, [])
|
| 26 |
);
|
| 27 |
|
| 28 |
const loadData = async () => {
|
| 29 |
const history = await getHistory();
|
| 30 |
-
setRecentScans(history.slice(0, 5));
|
| 31 |
|
| 32 |
const total = history.length;
|
| 33 |
const healthy = history.filter(item => item.analysisResult.isHealthy).length;
|
|
@@ -36,11 +39,18 @@ const HomeScreen = ({ route, navigation }) => {
|
|
| 36 |
setStats({ total, healthy, issues });
|
| 37 |
};
|
| 38 |
|
| 39 |
-
const
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
</View>
|
| 45 |
);
|
| 46 |
|
|
@@ -53,7 +63,7 @@ const HomeScreen = ({ route, navigation }) => {
|
|
| 53 |
|
| 54 |
let result = await ImagePicker.launchImageLibraryAsync({
|
| 55 |
mediaTypes: ImagePicker.MediaTypeOptions.Images,
|
| 56 |
-
allowsEditing:
|
| 57 |
quality: 1,
|
| 58 |
});
|
| 59 |
|
|
@@ -69,139 +79,158 @@ const HomeScreen = ({ route, navigation }) => {
|
|
| 69 |
return (
|
| 70 |
<SafeAreaView style={styles.container} edges={['right', 'left', 'top']}>
|
| 71 |
<StatusBar backgroundColor={COLORS.primary} barStyle="light-content" />
|
| 72 |
-
<ScrollView contentContainerStyle={styles.scrollContent} showsVerticalScrollIndicator={false}>
|
| 73 |
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
style={styles.headerLogo}
|
| 81 |
-
resizeMode="contain"
|
| 82 |
-
/>
|
| 83 |
-
</View>
|
| 84 |
-
<View style={styles.headerTextContainer}>
|
| 85 |
-
<Text style={styles.headerTitle}>GREEN DOCTOR</Text>
|
| 86 |
-
<Text style={styles.headerSubtitle}>{t.subtitle}</Text>
|
| 87 |
-
</View>
|
| 88 |
</View>
|
| 89 |
<View style={styles.headerActions}>
|
| 90 |
<TouchableOpacity
|
| 91 |
style={styles.headerActionButton}
|
| 92 |
onPress={() => navigation.navigate('Language')}
|
| 93 |
-
title={t.changeLanguage}
|
| 94 |
>
|
| 95 |
-
<Text style={styles.headerActionIcon}>
|
| 96 |
</TouchableOpacity>
|
| 97 |
<TouchableOpacity
|
| 98 |
style={styles.headerActionButton}
|
| 99 |
onPress={() => navigation.navigate('About', { language })}
|
| 100 |
-
title={t.aboutApp}
|
| 101 |
>
|
| 102 |
<Text style={styles.headerActionIcon}>ℹ️</Text>
|
| 103 |
</TouchableOpacity>
|
| 104 |
</View>
|
| 105 |
</View>
|
| 106 |
|
| 107 |
-
{/*
|
| 108 |
-
<View style={styles.
|
| 109 |
-
<
|
| 110 |
-
<
|
| 111 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 112 |
</View>
|
| 113 |
-
<Text style={styles.
|
| 114 |
-
<Text style={styles.
|
| 115 |
</TouchableOpacity>
|
| 116 |
|
| 117 |
-
<TouchableOpacity style={styles.
|
| 118 |
-
<View style={[styles.
|
| 119 |
-
<Text style={styles.
|
| 120 |
</View>
|
| 121 |
-
<Text style={styles.
|
| 122 |
-
<Text style={styles.
|
| 123 |
</TouchableOpacity>
|
| 124 |
</View>
|
| 125 |
|
| 126 |
-
{/*
|
| 127 |
-
|
| 128 |
-
style={styles.
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
<Text style={
|
| 133 |
</View>
|
| 134 |
-
|
| 135 |
-
<Text style={styles.historySubText}>{t.pastDiagnoses}</Text>
|
| 136 |
-
</TouchableOpacity>
|
| 137 |
|
| 138 |
-
{/*
|
| 139 |
-
<
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
<
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 149 |
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 156 |
</View>
|
| 157 |
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
|
|
|
|
|
|
| 163 |
</View>
|
| 164 |
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
<
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
<
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
)}
|
| 203 |
|
| 204 |
-
<View style={{ height:
|
| 205 |
</ScrollView>
|
| 206 |
</SafeAreaView>
|
| 207 |
);
|
|
@@ -212,235 +241,319 @@ const styles = StyleSheet.create({
|
|
| 212 |
flex: 1,
|
| 213 |
backgroundColor: COLORS.background,
|
| 214 |
},
|
| 215 |
-
|
| 216 |
-
padding: SIZES.padding,
|
| 217 |
-
paddingBottom: 20,
|
| 218 |
-
},
|
| 219 |
-
header: {
|
| 220 |
-
flexDirection: 'row',
|
| 221 |
-
alignItems: 'center',
|
| 222 |
-
justifyContent: 'space-between',
|
| 223 |
-
marginBottom: 20,
|
| 224 |
backgroundColor: COLORS.primary,
|
| 225 |
-
// Removed negative margins for SafeAreaView
|
| 226 |
paddingTop: 10,
|
| 227 |
-
paddingBottom:
|
| 228 |
paddingHorizontal: SIZES.padding,
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
shadowRadius: 4,
|
| 235 |
-
},
|
| 236 |
-
headerLeft: {
|
| 237 |
flexDirection: 'row',
|
|
|
|
| 238 |
alignItems: 'center',
|
|
|
|
| 239 |
},
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
height: 55,
|
| 243 |
-
backgroundColor: COLORS.white,
|
| 244 |
-
borderRadius: 27.5,
|
| 245 |
-
overflow: 'hidden',
|
| 246 |
-
justifyContent: 'center',
|
| 247 |
-
alignItems: 'center',
|
| 248 |
-
borderWidth: 2,
|
| 249 |
-
borderColor: 'rgba(255,255,255,0.3)',
|
| 250 |
},
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
|
|
|
|
|
|
| 254 |
},
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
|
|
|
|
|
|
| 258 |
},
|
| 259 |
headerActions: {
|
| 260 |
flexDirection: 'row',
|
|
|
|
| 261 |
},
|
| 262 |
headerActionButton: {
|
| 263 |
-
width:
|
| 264 |
-
height:
|
| 265 |
-
borderRadius:
|
| 266 |
-
backgroundColor: 'rgba(255,255,255,0.
|
| 267 |
justifyContent: 'center',
|
| 268 |
alignItems: 'center',
|
| 269 |
-
|
|
|
|
| 270 |
},
|
| 271 |
headerActionIcon: {
|
| 272 |
fontSize: 20,
|
| 273 |
},
|
| 274 |
-
|
| 275 |
flexDirection: 'row',
|
| 276 |
-
|
|
|
|
| 277 |
padding: 15,
|
| 278 |
-
borderRadius:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 279 |
alignItems: 'center',
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 292 |
alignItems: 'center',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 293 |
justifyContent: 'center',
|
| 294 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 295 |
},
|
| 296 |
-
|
| 297 |
fontSize: 16,
|
| 298 |
-
fontWeight: '
|
| 299 |
color: COLORS.text,
|
| 300 |
-
|
| 301 |
},
|
| 302 |
-
|
| 303 |
-
fontSize:
|
| 304 |
-
color: COLORS.
|
| 305 |
-
|
|
|
|
|
|
|
| 306 |
},
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 311 |
},
|
| 312 |
-
|
| 313 |
-
|
|
|
|
|
|
|
| 314 |
fontSize: 12,
|
|
|
|
|
|
|
|
|
|
| 315 |
},
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
|
| 321 |
},
|
| 322 |
-
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
padding: 20,
|
| 326 |
-
borderRadius: SIZES.radius,
|
| 327 |
-
alignItems: 'center',
|
| 328 |
-
elevation: 4,
|
| 329 |
-
shadowColor: '#000',
|
| 330 |
-
shadowOffset: { width: 0, height: 2 },
|
| 331 |
-
shadowOpacity: 0.1,
|
| 332 |
-
shadowRadius: 4,
|
| 333 |
},
|
| 334 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 335 |
width: 50,
|
| 336 |
height: 50,
|
| 337 |
-
borderRadius:
|
| 338 |
-
backgroundColor: '#004d40',
|
| 339 |
-
alignItems: 'center',
|
| 340 |
justifyContent: 'center',
|
| 341 |
-
|
|
|
|
| 342 |
},
|
| 343 |
-
|
| 344 |
fontSize: 24,
|
| 345 |
-
color: 'white',
|
| 346 |
},
|
| 347 |
-
|
| 348 |
-
|
| 349 |
-
|
|
|
|
|
|
|
|
|
|
| 350 |
color: COLORS.text,
|
| 351 |
-
marginBottom:
|
| 352 |
},
|
| 353 |
-
|
| 354 |
-
fontSize:
|
| 355 |
-
color: COLORS.
|
| 356 |
-
|
| 357 |
},
|
| 358 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 359 |
fontSize: 18,
|
| 360 |
-
|
| 361 |
-
|
| 362 |
-
|
|
|
|
| 363 |
},
|
| 364 |
-
|
|
|
|
|
|
|
|
|
|
| 365 |
flexDirection: 'row',
|
| 366 |
-
|
| 367 |
-
marginBottom: 25,
|
| 368 |
},
|
| 369 |
statCard: {
|
| 370 |
-
|
| 371 |
-
|
| 372 |
-
|
| 373 |
-
|
| 374 |
alignItems: 'center',
|
| 375 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 376 |
},
|
| 377 |
statNumber: {
|
| 378 |
-
fontSize:
|
| 379 |
-
fontWeight: '
|
| 380 |
-
|
| 381 |
},
|
| 382 |
statLabel: {
|
| 383 |
-
fontSize:
|
|
|
|
|
|
|
| 384 |
textAlign: 'center',
|
|
|
|
| 385 |
},
|
| 386 |
-
|
|
|
|
|
|
|
|
|
|
| 387 |
flexDirection: 'row',
|
| 388 |
-
backgroundColor: '#e0f2f1',
|
| 389 |
-
padding: 15,
|
| 390 |
-
borderRadius: SIZES.radius,
|
| 391 |
-
marginBottom: 25,
|
| 392 |
alignItems: 'center',
|
| 393 |
-
|
| 394 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 395 |
},
|
| 396 |
-
|
| 397 |
-
|
| 398 |
-
|
| 399 |
-
|
|
|
|
| 400 |
},
|
| 401 |
-
|
| 402 |
flex: 1,
|
| 403 |
-
|
| 404 |
-
fontSize: 14,
|
| 405 |
-
lineHeight: 20,
|
| 406 |
},
|
| 407 |
-
|
| 408 |
-
|
| 409 |
-
|
| 410 |
-
|
| 411 |
-
|
| 412 |
-
marginBottom: 10,
|
| 413 |
-
alignItems: 'center',
|
| 414 |
-
elevation: 2,
|
| 415 |
},
|
| 416 |
-
|
| 417 |
-
|
| 418 |
-
|
| 419 |
-
borderRadius: 10,
|
| 420 |
-
backgroundColor: '#eee',
|
| 421 |
-
marginRight: 15,
|
| 422 |
},
|
| 423 |
-
|
| 424 |
-
|
| 425 |
},
|
| 426 |
-
|
| 427 |
-
fontSize:
|
| 428 |
-
|
| 429 |
-
|
| 430 |
},
|
| 431 |
-
|
| 432 |
-
fontSize:
|
| 433 |
-
color: COLORS.
|
| 434 |
-
fontWeight: '
|
| 435 |
-
|
| 436 |
},
|
| 437 |
-
|
| 438 |
-
|
| 439 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 440 |
},
|
| 441 |
-
|
| 442 |
-
|
| 443 |
-
|
|
|
|
| 444 |
}
|
| 445 |
});
|
| 446 |
|
|
|
|
| 10 |
const { width } = Dimensions.get('window');
|
| 11 |
|
| 12 |
const HomeScreen = ({ route, navigation }) => {
|
|
|
|
| 13 |
const language = route.params?.language || 'en';
|
| 14 |
const [recentScans, setRecentScans] = useState([]);
|
| 15 |
const [stats, setStats] = useState({ total: 0, healthy: 0, issues: 0 });
|
| 16 |
+
const [tipIndex, setTipIndex] = useState(0);
|
| 17 |
|
|
|
|
| 18 |
const translations = TRANSLATIONS[language] || TRANSLATIONS['en'];
|
| 19 |
const t = translations;
|
| 20 |
|
| 21 |
useFocusEffect(
|
| 22 |
useCallback(() => {
|
| 23 |
loadData();
|
| 24 |
+
// Select a random tip from the pool
|
| 25 |
+
if (t.dailyTips) {
|
| 26 |
+
setTipIndex(Math.floor(Math.random() * t.dailyTips.length));
|
| 27 |
+
}
|
| 28 |
}, [])
|
| 29 |
);
|
| 30 |
|
| 31 |
const loadData = async () => {
|
| 32 |
const history = await getHistory();
|
| 33 |
+
setRecentScans(history.slice(0, 5));
|
| 34 |
|
| 35 |
const total = history.length;
|
| 36 |
const healthy = history.filter(item => item.analysisResult.isHealthy).length;
|
|
|
|
| 39 |
setStats({ total, healthy, issues });
|
| 40 |
};
|
| 41 |
|
| 42 |
+
const getGreeting = () => {
|
| 43 |
+
const hour = new Date().getHours();
|
| 44 |
+
if (hour < 12) return language === 'ta' ? "காலை வணக்கம்" : "Good Morning";
|
| 45 |
+
if (hour < 17) return language === 'ta' ? "மதிய வணக்கம்" : "Good Afternoon";
|
| 46 |
+
return language === 'ta' ? "மாலை வணக்கம்" : "Good Evening";
|
| 47 |
+
};
|
| 48 |
+
|
| 49 |
+
const StatCard = ({ icon, number, label, color }) => (
|
| 50 |
+
<View style={[styles.statCard, { borderBottomColor: color }]}>
|
| 51 |
+
<Text style={styles.statIcon}>{icon}</Text>
|
| 52 |
+
<Text style={styles.statNumber}>{number}</Text>
|
| 53 |
+
<Text style={styles.statLabel}>{label}</Text>
|
| 54 |
</View>
|
| 55 |
);
|
| 56 |
|
|
|
|
| 63 |
|
| 64 |
let result = await ImagePicker.launchImageLibraryAsync({
|
| 65 |
mediaTypes: ImagePicker.MediaTypeOptions.Images,
|
| 66 |
+
allowsEditing: false,
|
| 67 |
quality: 1,
|
| 68 |
});
|
| 69 |
|
|
|
|
| 79 |
return (
|
| 80 |
<SafeAreaView style={styles.container} edges={['right', 'left', 'top']}>
|
| 81 |
<StatusBar backgroundColor={COLORS.primary} barStyle="light-content" />
|
|
|
|
| 82 |
|
| 83 |
+
{/* Immersive Header */}
|
| 84 |
+
<View style={styles.premiumHeader}>
|
| 85 |
+
<View style={styles.headerTopRow}>
|
| 86 |
+
<View style={styles.headerTitleContainer}>
|
| 87 |
+
<Text style={styles.greetingText}>{getGreeting()},</Text>
|
| 88 |
+
<Text style={styles.brandTitle}>GREEN DOCTOR</Text>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
</View>
|
| 90 |
<View style={styles.headerActions}>
|
| 91 |
<TouchableOpacity
|
| 92 |
style={styles.headerActionButton}
|
| 93 |
onPress={() => navigation.navigate('Language')}
|
|
|
|
| 94 |
>
|
| 95 |
+
<Text style={[styles.headerActionIcon, { fontSize: 13, fontWeight: '900', color: COLORS.white }]}>A/அ</Text>
|
| 96 |
</TouchableOpacity>
|
| 97 |
<TouchableOpacity
|
| 98 |
style={styles.headerActionButton}
|
| 99 |
onPress={() => navigation.navigate('About', { language })}
|
|
|
|
| 100 |
>
|
| 101 |
<Text style={styles.headerActionIcon}>ℹ️</Text>
|
| 102 |
</TouchableOpacity>
|
| 103 |
</View>
|
| 104 |
</View>
|
| 105 |
|
| 106 |
+
{/* Main Hero Card */}
|
| 107 |
+
<View style={styles.heroCard}>
|
| 108 |
+
<View style={styles.logoCircleLg}>
|
| 109 |
+
<Image
|
| 110 |
+
source={require('../assets/logo.png')}
|
| 111 |
+
style={styles.heroLogo}
|
| 112 |
+
resizeMode="contain"
|
| 113 |
+
/>
|
| 114 |
+
</View>
|
| 115 |
+
<View style={styles.heroImpact}>
|
| 116 |
+
<Text style={styles.heroTagline}>{t.subtitle}</Text>
|
| 117 |
+
<View style={styles.activeLabelContainer}>
|
| 118 |
+
<View style={styles.pulseIndicator} />
|
| 119 |
+
<Text style={styles.pulseText}>AI SYSTEM ACTIVE</Text>
|
| 120 |
+
</View>
|
| 121 |
+
</View>
|
| 122 |
+
</View>
|
| 123 |
+
</View>
|
| 124 |
+
|
| 125 |
+
<ScrollView contentContainerStyle={styles.scrollContent} showsVerticalScrollIndicator={false}>
|
| 126 |
+
{/* Core Scanning Actions */}
|
| 127 |
+
<View style={styles.primaryActionRow}>
|
| 128 |
+
<TouchableOpacity style={styles.primaryActionCard} onPress={takePhoto}>
|
| 129 |
+
<View style={styles.actionIconContainer}>
|
| 130 |
+
<Text style={styles.actionEmoji}>📷</Text>
|
| 131 |
</View>
|
| 132 |
+
<Text style={styles.actionTitleText}>{t.scanPlant}</Text>
|
| 133 |
+
<Text style={styles.actionDescText}>{t.detectDiseases}</Text>
|
| 134 |
</TouchableOpacity>
|
| 135 |
|
| 136 |
+
<TouchableOpacity style={[styles.primaryActionCard, { backgroundColor: COLORS.secondary + '10' }]} onPress={pickImage}>
|
| 137 |
+
<View style={[styles.actionIconContainer, { backgroundColor: COLORS.secondary }]}>
|
| 138 |
+
<Text style={styles.actionEmoji}>🖼️</Text>
|
| 139 |
</View>
|
| 140 |
+
<Text style={styles.actionTitleText}>{t.pickFromGallery}</Text>
|
| 141 |
+
<Text style={styles.actionDescText}>{t.detectDiseases}</Text>
|
| 142 |
</TouchableOpacity>
|
| 143 |
</View>
|
| 144 |
|
| 145 |
+
{/* Daily Expert Tips integrated from Translations */}
|
| 146 |
+
{t.dailyTips && (
|
| 147 |
+
<View style={styles.proTipCard}>
|
| 148 |
+
<View style={styles.tipHeaderBox}>
|
| 149 |
+
<Text style={styles.tipLabel}>💡 {language === 'ta' ? "நிபுணரின் குறிப்பு" : "EXPERT TIP"}</Text>
|
| 150 |
+
</View>
|
| 151 |
+
<Text style={styles.tipMainText}>{t.dailyTips[tipIndex]}</Text>
|
| 152 |
</View>
|
| 153 |
+
)}
|
|
|
|
|
|
|
| 154 |
|
| 155 |
+
{/* Secondary Hub Actions */}
|
| 156 |
+
<View style={styles.listActionArea}>
|
| 157 |
+
<TouchableOpacity
|
| 158 |
+
style={styles.fullWidthItem}
|
| 159 |
+
onPress={() => navigation.navigate('History', { language })}
|
| 160 |
+
>
|
| 161 |
+
<View style={[styles.itemIconBox, { backgroundColor: '#E3F2FD' }]}>
|
| 162 |
+
<Text style={styles.itemIcon}>↺</Text>
|
| 163 |
+
</View>
|
| 164 |
+
<View style={styles.itemInfo}>
|
| 165 |
+
<Text style={styles.itemTitle}>{t.history}</Text>
|
| 166 |
+
<Text style={styles.itemSubtitle}>{t.pastDiagnoses}</Text>
|
| 167 |
+
</View>
|
| 168 |
+
<Text style={styles.itemChevron}>›</Text>
|
| 169 |
+
</TouchableOpacity>
|
| 170 |
|
| 171 |
+
<TouchableOpacity
|
| 172 |
+
style={styles.fullWidthItem}
|
| 173 |
+
onPress={() => navigation.navigate('Schemes', { language })}
|
| 174 |
+
>
|
| 175 |
+
<View style={[styles.itemIconBox, { backgroundColor: '#FFF3E0' }]}>
|
| 176 |
+
<Text style={styles.itemIcon}>📋</Text>
|
| 177 |
+
</View>
|
| 178 |
+
<View style={styles.itemInfo}>
|
| 179 |
+
<Text style={styles.itemTitle}>{t.govtSchemes}</Text>
|
| 180 |
+
<Text style={styles.itemSubtitle}>{t.viewSchemes}</Text>
|
| 181 |
+
</View>
|
| 182 |
+
<Text style={[styles.itemChevron, { color: '#FFB74D' }]}>›</Text>
|
| 183 |
+
</TouchableOpacity>
|
| 184 |
</View>
|
| 185 |
|
| 186 |
+
<View style={styles.statsBlock}>
|
| 187 |
+
<Text style={styles.sectionHeading}>{t.yourStats}</Text>
|
| 188 |
+
<View style={styles.statsRow}>
|
| 189 |
+
<StatCard icon="📈" number={stats.total} label={t.totalScans} color={COLORS.info} />
|
| 190 |
+
<StatCard icon="🌿" number={stats.healthy} label={t.healthyPlants} color={COLORS.success} />
|
| 191 |
+
<StatCard icon="⚠" number={stats.issues} label={t.issuesDetected} color={COLORS.warning} />
|
| 192 |
+
</View>
|
| 193 |
</View>
|
| 194 |
|
| 195 |
+
<View style={styles.recentBlock}>
|
| 196 |
+
<Text style={styles.sectionHeading}>{t.recentScans}</Text>
|
| 197 |
+
{recentScans.length === 0 ? (
|
| 198 |
+
<View style={styles.noRecentBox}>
|
| 199 |
+
<Text style={styles.noRecentText}>{t.noHistory}</Text>
|
| 200 |
+
</View>
|
| 201 |
+
) : (
|
| 202 |
+
recentScans.map((item, index) => (
|
| 203 |
+
<TouchableOpacity
|
| 204 |
+
key={index}
|
| 205 |
+
style={styles.recentPreviewCard}
|
| 206 |
+
onPress={() => navigation.navigate('Result', {
|
| 207 |
+
imageUri: item.imageUri,
|
| 208 |
+
analysisResult: item.analysisResult,
|
| 209 |
+
language: language
|
| 210 |
+
})}
|
| 211 |
+
>
|
| 212 |
+
<Image source={{ uri: item.imageUri }} style={styles.previewImage} />
|
| 213 |
+
<View style={styles.previewBio}>
|
| 214 |
+
<Text style={styles.previewCrop}>{item.analysisResult.crop}</Text>
|
| 215 |
+
<Text style={[
|
| 216 |
+
styles.previewDisease,
|
| 217 |
+
{ color: item.analysisResult.isHealthy ? COLORS.success : COLORS.error }
|
| 218 |
+
]}>
|
| 219 |
+
{item.analysisResult.name[language]}
|
| 220 |
+
</Text>
|
| 221 |
+
</View>
|
| 222 |
+
<View style={styles.previewAction}>
|
| 223 |
+
<Text style={styles.previewTime}>
|
| 224 |
+
{new Date(item.timestamp).toLocaleDateString(undefined, { month: 'short', day: 'numeric' })}
|
| 225 |
+
</Text>
|
| 226 |
+
<Text style={styles.previewArrow}>→</Text>
|
| 227 |
+
</View>
|
| 228 |
+
</TouchableOpacity>
|
| 229 |
+
))
|
| 230 |
+
)}
|
| 231 |
+
</View>
|
|
|
|
| 232 |
|
| 233 |
+
<View style={{ height: 40 }} />
|
| 234 |
</ScrollView>
|
| 235 |
</SafeAreaView>
|
| 236 |
);
|
|
|
|
| 241 |
flex: 1,
|
| 242 |
backgroundColor: COLORS.background,
|
| 243 |
},
|
| 244 |
+
premiumHeader: {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 245 |
backgroundColor: COLORS.primary,
|
|
|
|
| 246 |
paddingTop: 10,
|
| 247 |
+
paddingBottom: 30,
|
| 248 |
paddingHorizontal: SIZES.padding,
|
| 249 |
+
borderBottomLeftRadius: 35,
|
| 250 |
+
borderBottomRightRadius: 35,
|
| 251 |
+
...COLORS.shadow.lg,
|
| 252 |
+
},
|
| 253 |
+
headerTopRow: {
|
|
|
|
|
|
|
|
|
|
| 254 |
flexDirection: 'row',
|
| 255 |
+
justifyContent: 'space-between',
|
| 256 |
alignItems: 'center',
|
| 257 |
+
marginBottom: 25,
|
| 258 |
},
|
| 259 |
+
headerTitleContainer: {
|
| 260 |
+
flex: 1,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 261 |
},
|
| 262 |
+
greetingText: {
|
| 263 |
+
color: 'rgba(255,255,255,0.7)',
|
| 264 |
+
fontSize: 14,
|
| 265 |
+
fontWeight: '600',
|
| 266 |
+
letterSpacing: 0.5,
|
| 267 |
},
|
| 268 |
+
brandTitle: {
|
| 269 |
+
color: COLORS.white,
|
| 270 |
+
fontSize: 22,
|
| 271 |
+
fontWeight: '900',
|
| 272 |
+
letterSpacing: 1,
|
| 273 |
},
|
| 274 |
headerActions: {
|
| 275 |
flexDirection: 'row',
|
| 276 |
+
gap: 10,
|
| 277 |
},
|
| 278 |
headerActionButton: {
|
| 279 |
+
width: 44,
|
| 280 |
+
height: 44,
|
| 281 |
+
borderRadius: 14,
|
| 282 |
+
backgroundColor: 'rgba(255,255,255,0.15)',
|
| 283 |
justifyContent: 'center',
|
| 284 |
alignItems: 'center',
|
| 285 |
+
borderWidth: 1,
|
| 286 |
+
borderColor: 'rgba(255,255,255,0.2)',
|
| 287 |
},
|
| 288 |
headerActionIcon: {
|
| 289 |
fontSize: 20,
|
| 290 |
},
|
| 291 |
+
heroCard: {
|
| 292 |
flexDirection: 'row',
|
| 293 |
+
alignItems: 'center',
|
| 294 |
+
backgroundColor: 'rgba(255,255,255,0.1)',
|
| 295 |
padding: 15,
|
| 296 |
+
borderRadius: 25,
|
| 297 |
+
borderWidth: 1,
|
| 298 |
+
borderColor: 'rgba(255,255,255,0.1)',
|
| 299 |
+
},
|
| 300 |
+
logoCircleLg: {
|
| 301 |
+
width: 70,
|
| 302 |
+
height: 70,
|
| 303 |
+
borderRadius: 35,
|
| 304 |
+
backgroundColor: COLORS.white,
|
| 305 |
+
justifyContent: 'center',
|
| 306 |
alignItems: 'center',
|
| 307 |
+
...COLORS.shadow.md,
|
| 308 |
+
},
|
| 309 |
+
heroLogo: {
|
| 310 |
+
width: '80%',
|
| 311 |
+
height: '80%',
|
| 312 |
+
},
|
| 313 |
+
heroImpact: {
|
| 314 |
+
marginLeft: 15,
|
| 315 |
+
flex: 1,
|
| 316 |
+
},
|
| 317 |
+
heroTagline: {
|
| 318 |
+
color: COLORS.white,
|
| 319 |
+
fontSize: 15,
|
| 320 |
+
fontWeight: '700',
|
| 321 |
+
marginBottom: 6,
|
| 322 |
+
},
|
| 323 |
+
activeLabelContainer: {
|
| 324 |
+
flexDirection: 'row',
|
| 325 |
alignItems: 'center',
|
| 326 |
+
backgroundColor: 'rgba(0,0,0,0.2)',
|
| 327 |
+
alignSelf: 'flex-start',
|
| 328 |
+
paddingHorizontal: 8,
|
| 329 |
+
paddingVertical: 4,
|
| 330 |
+
borderRadius: 10,
|
| 331 |
+
},
|
| 332 |
+
pulseIndicator: {
|
| 333 |
+
width: 6,
|
| 334 |
+
height: 6,
|
| 335 |
+
borderRadius: 3,
|
| 336 |
+
backgroundColor: '#4ade80',
|
| 337 |
+
marginRight: 6,
|
| 338 |
+
},
|
| 339 |
+
pulseText: {
|
| 340 |
+
color: '#4ade80',
|
| 341 |
+
fontSize: 9,
|
| 342 |
+
fontWeight: '900',
|
| 343 |
+
letterSpacing: 1,
|
| 344 |
+
},
|
| 345 |
+
scrollContent: {
|
| 346 |
+
padding: SIZES.padding,
|
| 347 |
+
},
|
| 348 |
+
primaryActionRow: {
|
| 349 |
+
flexDirection: 'row',
|
| 350 |
+
gap: 15,
|
| 351 |
+
marginTop: 20,
|
| 352 |
+
marginBottom: 25,
|
| 353 |
+
},
|
| 354 |
+
primaryActionCard: {
|
| 355 |
+
flex: 1,
|
| 356 |
+
backgroundColor: COLORS.primary + '10',
|
| 357 |
+
borderRadius: 25,
|
| 358 |
+
padding: 20,
|
| 359 |
+
alignItems: 'center',
|
| 360 |
+
borderWidth: 1,
|
| 361 |
+
borderColor: COLORS.border,
|
| 362 |
+
},
|
| 363 |
+
actionIconContainer: {
|
| 364 |
+
width: 60,
|
| 365 |
+
height: 60,
|
| 366 |
+
borderRadius: 30,
|
| 367 |
+
backgroundColor: COLORS.primary,
|
| 368 |
justifyContent: 'center',
|
| 369 |
+
alignItems: 'center',
|
| 370 |
+
marginBottom: 12,
|
| 371 |
+
...COLORS.shadow.md,
|
| 372 |
+
},
|
| 373 |
+
actionEmoji: {
|
| 374 |
+
fontSize: 28,
|
| 375 |
},
|
| 376 |
+
actionTitleText: {
|
| 377 |
fontSize: 16,
|
| 378 |
+
fontWeight: '800',
|
| 379 |
color: COLORS.text,
|
| 380 |
+
textAlign: 'center',
|
| 381 |
},
|
| 382 |
+
actionDescText: {
|
| 383 |
+
fontSize: 11,
|
| 384 |
+
color: COLORS.textLight,
|
| 385 |
+
textAlign: 'center',
|
| 386 |
+
marginTop: 4,
|
| 387 |
+
fontWeight: '600',
|
| 388 |
},
|
| 389 |
+
proTipCard: {
|
| 390 |
+
backgroundColor: COLORS.surface,
|
| 391 |
+
borderRadius: 25,
|
| 392 |
+
padding: 20,
|
| 393 |
+
marginBottom: 25,
|
| 394 |
+
borderLeftWidth: 6,
|
| 395 |
+
borderLeftColor: COLORS.secondary,
|
| 396 |
+
...COLORS.shadow.md,
|
| 397 |
},
|
| 398 |
+
tipHeaderBox: {
|
| 399 |
+
marginBottom: 10,
|
| 400 |
+
},
|
| 401 |
+
tipLabel: {
|
| 402 |
fontSize: 12,
|
| 403 |
+
fontWeight: '900',
|
| 404 |
+
color: COLORS.secondary,
|
| 405 |
+
letterSpacing: 1,
|
| 406 |
},
|
| 407 |
+
tipMainText: {
|
| 408 |
+
fontSize: 15,
|
| 409 |
+
color: COLORS.text,
|
| 410 |
+
lineHeight: 22,
|
| 411 |
+
fontWeight: '500',
|
| 412 |
},
|
| 413 |
+
listActionArea: {
|
| 414 |
+
gap: 12,
|
| 415 |
+
marginBottom: 30,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 416 |
},
|
| 417 |
+
fullWidthItem: {
|
| 418 |
+
flexDirection: 'row',
|
| 419 |
+
alignItems: 'center',
|
| 420 |
+
backgroundColor: COLORS.surface,
|
| 421 |
+
padding: 16,
|
| 422 |
+
borderRadius: 22,
|
| 423 |
+
borderWidth: 1,
|
| 424 |
+
borderColor: COLORS.border,
|
| 425 |
+
...COLORS.shadow.sm,
|
| 426 |
+
},
|
| 427 |
+
itemIconBox: {
|
| 428 |
width: 50,
|
| 429 |
height: 50,
|
| 430 |
+
borderRadius: 15,
|
|
|
|
|
|
|
| 431 |
justifyContent: 'center',
|
| 432 |
+
alignItems: 'center',
|
| 433 |
+
marginRight: 15,
|
| 434 |
},
|
| 435 |
+
itemIcon: {
|
| 436 |
fontSize: 24,
|
|
|
|
| 437 |
},
|
| 438 |
+
itemInfo: {
|
| 439 |
+
flex: 1,
|
| 440 |
+
},
|
| 441 |
+
itemTitle: {
|
| 442 |
+
fontSize: 17,
|
| 443 |
+
fontWeight: '800',
|
| 444 |
color: COLORS.text,
|
| 445 |
+
marginBottom: 2,
|
| 446 |
},
|
| 447 |
+
itemSubtitle: {
|
| 448 |
+
fontSize: 12,
|
| 449 |
+
color: COLORS.textLight,
|
| 450 |
+
fontWeight: '500',
|
| 451 |
},
|
| 452 |
+
itemChevron: {
|
| 453 |
+
fontSize: 28,
|
| 454 |
+
color: COLORS.primary,
|
| 455 |
+
opacity: 0.3,
|
| 456 |
+
fontWeight: '300',
|
| 457 |
+
},
|
| 458 |
+
sectionHeading: {
|
| 459 |
fontSize: 18,
|
| 460 |
+
fontWeight: '900',
|
| 461 |
+
color: COLORS.primaryDark,
|
| 462 |
+
marginBottom: 15,
|
| 463 |
+
letterSpacing: 0.5,
|
| 464 |
},
|
| 465 |
+
statsBlock: {
|
| 466 |
+
marginBottom: 30,
|
| 467 |
+
},
|
| 468 |
+
statsRow: {
|
| 469 |
flexDirection: 'row',
|
| 470 |
+
gap: 12,
|
|
|
|
| 471 |
},
|
| 472 |
statCard: {
|
| 473 |
+
flex: 1,
|
| 474 |
+
backgroundColor: COLORS.surface,
|
| 475 |
+
borderRadius: 20,
|
| 476 |
+
padding: 15,
|
| 477 |
alignItems: 'center',
|
| 478 |
+
borderBottomWidth: 4,
|
| 479 |
+
...COLORS.shadow.sm,
|
| 480 |
+
},
|
| 481 |
+
statIcon: {
|
| 482 |
+
fontSize: 24,
|
| 483 |
+
marginBottom: 8,
|
| 484 |
},
|
| 485 |
statNumber: {
|
| 486 |
+
fontSize: 22,
|
| 487 |
+
fontWeight: '900',
|
| 488 |
+
color: COLORS.text,
|
| 489 |
},
|
| 490 |
statLabel: {
|
| 491 |
+
fontSize: 10,
|
| 492 |
+
fontWeight: '700',
|
| 493 |
+
color: COLORS.textLight,
|
| 494 |
textAlign: 'center',
|
| 495 |
+
marginTop: 2,
|
| 496 |
},
|
| 497 |
+
recentBlock: {
|
| 498 |
+
marginBottom: 20,
|
| 499 |
+
},
|
| 500 |
+
recentPreviewCard: {
|
| 501 |
flexDirection: 'row',
|
|
|
|
|
|
|
|
|
|
|
|
|
| 502 |
alignItems: 'center',
|
| 503 |
+
backgroundColor: COLORS.surface,
|
| 504 |
+
padding: 12,
|
| 505 |
+
borderRadius: 20,
|
| 506 |
+
marginBottom: 10,
|
| 507 |
+
borderWidth: 1,
|
| 508 |
+
borderColor: COLORS.border,
|
| 509 |
},
|
| 510 |
+
previewImage: {
|
| 511 |
+
width: 54,
|
| 512 |
+
height: 54,
|
| 513 |
+
borderRadius: 12,
|
| 514 |
+
backgroundColor: COLORS.background,
|
| 515 |
},
|
| 516 |
+
previewBio: {
|
| 517 |
flex: 1,
|
| 518 |
+
marginLeft: 15,
|
|
|
|
|
|
|
| 519 |
},
|
| 520 |
+
previewCrop: {
|
| 521 |
+
fontSize: 13,
|
| 522 |
+
fontWeight: '700',
|
| 523 |
+
color: COLORS.textLight,
|
| 524 |
+
textTransform: 'uppercase',
|
|
|
|
|
|
|
|
|
|
| 525 |
},
|
| 526 |
+
previewDisease: {
|
| 527 |
+
fontSize: 16,
|
| 528 |
+
fontWeight: '800',
|
|
|
|
|
|
|
|
|
|
| 529 |
},
|
| 530 |
+
previewAction: {
|
| 531 |
+
alignItems: 'flex-end',
|
| 532 |
},
|
| 533 |
+
previewTime: {
|
| 534 |
+
fontSize: 11,
|
| 535 |
+
fontWeight: '700',
|
| 536 |
+
color: COLORS.textLight,
|
| 537 |
},
|
| 538 |
+
previewArrow: {
|
| 539 |
+
fontSize: 18,
|
| 540 |
+
color: COLORS.primary,
|
| 541 |
+
fontWeight: '900',
|
| 542 |
+
marginTop: 2,
|
| 543 |
},
|
| 544 |
+
noRecentBox: {
|
| 545 |
+
padding: 40,
|
| 546 |
+
alignItems: 'center',
|
| 547 |
+
backgroundColor: COLORS.surface,
|
| 548 |
+
borderRadius: 20,
|
| 549 |
+
borderStyle: 'dashed',
|
| 550 |
+
borderWidth: 1,
|
| 551 |
+
borderColor: COLORS.border,
|
| 552 |
},
|
| 553 |
+
noRecentText: {
|
| 554 |
+
color: COLORS.textLight,
|
| 555 |
+
fontStyle: 'italic',
|
| 556 |
+
fontSize: 14,
|
| 557 |
}
|
| 558 |
});
|
| 559 |
|
screens/LanguageScreen.js
CHANGED
|
@@ -1,97 +1,221 @@
|
|
| 1 |
import React from 'react';
|
| 2 |
-
import { View, Text, TouchableOpacity, StyleSheet, Image } from 'react-native';
|
| 3 |
-
import {
|
|
|
|
| 4 |
import { TRANSLATIONS } from '../constants/translations';
|
| 5 |
|
|
|
|
|
|
|
| 6 |
const LanguageScreen = ({ navigation }) => {
|
| 7 |
const handleLanguageSelect = (lang) => {
|
| 8 |
-
// Navigate to Home with selected language
|
| 9 |
navigation.reset({
|
| 10 |
index: 0,
|
| 11 |
routes: [{ name: 'Home', params: { language: lang } }],
|
| 12 |
});
|
| 13 |
};
|
| 14 |
|
| 15 |
-
|
| 16 |
-
<
|
| 17 |
-
{
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
</View>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
|
| 26 |
-
<View style={styles.
|
|
|
|
|
|
|
| 27 |
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
|
| 32 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
|
|
|
| 41 |
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
);
|
| 50 |
};
|
| 51 |
|
| 52 |
const styles = StyleSheet.create({
|
| 53 |
container: {
|
| 54 |
flex: 1,
|
| 55 |
-
backgroundColor: COLORS.
|
| 56 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
justifyContent: 'center',
|
| 58 |
-
|
| 59 |
},
|
| 60 |
-
|
|
|
|
|
|
|
|
|
|
| 61 |
alignItems: 'center',
|
| 62 |
-
marginBottom: 20,
|
| 63 |
-
height: 180,
|
| 64 |
},
|
| 65 |
logoImage: {
|
| 66 |
width: 300,
|
| 67 |
height: '100%',
|
| 68 |
},
|
| 69 |
-
|
| 70 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 71 |
},
|
| 72 |
title: {
|
| 73 |
-
color:
|
| 74 |
-
fontSize:
|
|
|
|
| 75 |
textAlign: 'center',
|
| 76 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
},
|
| 78 |
-
|
|
|
|
|
|
|
|
|
|
| 79 |
width: '100%',
|
| 80 |
-
maxWidth: 250,
|
| 81 |
-
paddingVertical: 15,
|
| 82 |
-
borderRadius: 25,
|
| 83 |
borderWidth: 1,
|
| 84 |
-
borderColor:
|
| 85 |
-
|
| 86 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 87 |
alignItems: 'center',
|
| 88 |
},
|
| 89 |
-
|
|
|
|
| 90 |
fontSize: 20,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
fontWeight: 'bold',
|
| 92 |
-
|
| 93 |
-
letterSpacing: 1,
|
| 94 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
});
|
| 96 |
|
| 97 |
export default LanguageScreen;
|
|
|
|
| 1 |
import React from 'react';
|
| 2 |
+
import { View, Text, TouchableOpacity, StyleSheet, Image, Dimensions, StatusBar } from 'react-native';
|
| 3 |
+
import { SafeAreaView } from 'react-native-safe-area-context';
|
| 4 |
+
import { COLORS, SIZES, FONTS } from '../constants/theme';
|
| 5 |
import { TRANSLATIONS } from '../constants/translations';
|
| 6 |
|
| 7 |
+
const { width } = Dimensions.get('window');
|
| 8 |
+
|
| 9 |
const LanguageScreen = ({ navigation }) => {
|
| 10 |
const handleLanguageSelect = (lang) => {
|
|
|
|
| 11 |
navigation.reset({
|
| 12 |
index: 0,
|
| 13 |
routes: [{ name: 'Home', params: { language: lang } }],
|
| 14 |
});
|
| 15 |
};
|
| 16 |
|
| 17 |
+
const LanguageTile = ({ lang, code, label, subLabel, icon }) => (
|
| 18 |
+
<TouchableOpacity
|
| 19 |
+
style={styles.langTile}
|
| 20 |
+
onPress={() => handleLanguageSelect(code)}
|
| 21 |
+
activeOpacity={0.7}
|
| 22 |
+
>
|
| 23 |
+
<View style={styles.tileContent}>
|
| 24 |
+
<View style={styles.iconCircle}>
|
| 25 |
+
<Text style={styles.tileIcon}>{icon}</Text>
|
| 26 |
+
</View>
|
| 27 |
+
<View style={styles.tileTextContainer}>
|
| 28 |
+
<Text style={styles.tileLabel}>{label}</Text>
|
| 29 |
+
<Text style={styles.tileSubLabel}>{subLabel}</Text>
|
| 30 |
+
</View>
|
| 31 |
+
<View style={styles.arrowContainer}>
|
| 32 |
+
<Text style={styles.arrowIcon}>→</Text>
|
| 33 |
+
</View>
|
| 34 |
</View>
|
| 35 |
+
</TouchableOpacity>
|
| 36 |
+
);
|
| 37 |
+
|
| 38 |
+
return (
|
| 39 |
+
<SafeAreaView style={styles.container}>
|
| 40 |
+
<StatusBar backgroundColor={COLORS.background} barStyle="dark-content" />
|
| 41 |
|
| 42 |
+
<View style={styles.content}>
|
| 43 |
+
{/* Visual Accent */}
|
| 44 |
+
<View style={styles.topAccent} />
|
| 45 |
|
| 46 |
+
{/* Logo Section */}
|
| 47 |
+
<View style={styles.logoWrapper}>
|
| 48 |
+
<Image
|
| 49 |
+
source={require('../assets/logo.png')}
|
| 50 |
+
style={styles.logoImage}
|
| 51 |
+
resizeMode="contain"
|
| 52 |
+
/>
|
| 53 |
+
</View>
|
| 54 |
|
| 55 |
+
{/* Header Section */}
|
| 56 |
+
<View style={styles.header}>
|
| 57 |
+
<Text style={styles.welcomeText}>WELCOME / வரவேற்கிறோம்</Text>
|
| 58 |
+
<Text style={styles.title}>
|
| 59 |
+
{TRANSLATIONS.en.selectLanguage} / {TRANSLATIONS.ta.selectLanguage}
|
| 60 |
+
</Text>
|
| 61 |
+
</View>
|
| 62 |
|
| 63 |
+
{/* Selection Section */}
|
| 64 |
+
<View style={styles.selectionGrid}>
|
| 65 |
+
<LanguageTile
|
| 66 |
+
code="en"
|
| 67 |
+
label="English"
|
| 68 |
+
subLabel="Continue in English"
|
| 69 |
+
icon="🇬🇧"
|
| 70 |
+
/>
|
| 71 |
|
| 72 |
+
<LanguageTile
|
| 73 |
+
code="ta"
|
| 74 |
+
label="தமிழ்"
|
| 75 |
+
subLabel="தமிழில் தொடரவும்"
|
| 76 |
+
icon="🇮🇳"
|
| 77 |
+
/>
|
| 78 |
+
</View>
|
| 79 |
+
|
| 80 |
+
<View style={styles.footer}>
|
| 81 |
+
<Text style={styles.footerText}>Green Doctor v1.0.0</Text>
|
| 82 |
+
<View style={styles.divider} />
|
| 83 |
+
<Text style={styles.powerText}>Empowering Farmers with AI</Text>
|
| 84 |
+
</View>
|
| 85 |
+
</View>
|
| 86 |
+
</SafeAreaView>
|
| 87 |
);
|
| 88 |
};
|
| 89 |
|
| 90 |
const styles = StyleSheet.create({
|
| 91 |
container: {
|
| 92 |
flex: 1,
|
| 93 |
+
backgroundColor: COLORS.background,
|
| 94 |
+
},
|
| 95 |
+
topAccent: {
|
| 96 |
+
position: 'absolute',
|
| 97 |
+
top: -100,
|
| 98 |
+
right: -100,
|
| 99 |
+
width: 300,
|
| 100 |
+
height: 300,
|
| 101 |
+
borderRadius: 150,
|
| 102 |
+
backgroundColor: COLORS.primaryLight + '10',
|
| 103 |
+
},
|
| 104 |
+
content: {
|
| 105 |
+
flex: 1,
|
| 106 |
+
paddingHorizontal: SIZES.padding * 1.5,
|
| 107 |
justifyContent: 'center',
|
| 108 |
+
alignItems: 'center',
|
| 109 |
},
|
| 110 |
+
logoWrapper: {
|
| 111 |
+
marginBottom: 30,
|
| 112 |
+
height: 160,
|
| 113 |
+
width: '100%',
|
| 114 |
alignItems: 'center',
|
|
|
|
|
|
|
| 115 |
},
|
| 116 |
logoImage: {
|
| 117 |
width: 300,
|
| 118 |
height: '100%',
|
| 119 |
},
|
| 120 |
+
header: {
|
| 121 |
+
alignItems: 'center',
|
| 122 |
+
marginBottom: 40,
|
| 123 |
+
},
|
| 124 |
+
welcomeText: {
|
| 125 |
+
fontSize: 12,
|
| 126 |
+
color: COLORS.primary,
|
| 127 |
+
fontWeight: '800',
|
| 128 |
+
letterSpacing: 3,
|
| 129 |
+
marginBottom: 12,
|
| 130 |
+
textTransform: 'uppercase',
|
| 131 |
},
|
| 132 |
title: {
|
| 133 |
+
color: COLORS.text,
|
| 134 |
+
fontSize: SIZES.h3,
|
| 135 |
+
fontWeight: '600',
|
| 136 |
textAlign: 'center',
|
| 137 |
+
lineHeight: 26,
|
| 138 |
+
opacity: 0.8,
|
| 139 |
+
},
|
| 140 |
+
selectionGrid: {
|
| 141 |
+
width: '100%',
|
| 142 |
+
gap: 20,
|
| 143 |
},
|
| 144 |
+
langTile: {
|
| 145 |
+
backgroundColor: COLORS.surface,
|
| 146 |
+
borderRadius: SIZES.radius,
|
| 147 |
+
padding: SIZES.padding,
|
| 148 |
width: '100%',
|
|
|
|
|
|
|
|
|
|
| 149 |
borderWidth: 1,
|
| 150 |
+
borderColor: COLORS.border,
|
| 151 |
+
...COLORS.shadow.md,
|
| 152 |
+
},
|
| 153 |
+
tileContent: {
|
| 154 |
+
flexDirection: 'row',
|
| 155 |
+
alignItems: 'center',
|
| 156 |
+
},
|
| 157 |
+
iconCircle: {
|
| 158 |
+
width: 56,
|
| 159 |
+
height: 56,
|
| 160 |
+
borderRadius: 28,
|
| 161 |
+
backgroundColor: COLORS.background,
|
| 162 |
+
justifyContent: 'center',
|
| 163 |
+
alignItems: 'center',
|
| 164 |
+
marginRight: 16,
|
| 165 |
+
},
|
| 166 |
+
tileIcon: {
|
| 167 |
+
fontSize: 28,
|
| 168 |
+
},
|
| 169 |
+
tileTextContainer: {
|
| 170 |
+
flex: 1,
|
| 171 |
+
},
|
| 172 |
+
tileLabel: {
|
| 173 |
+
fontSize: 22,
|
| 174 |
+
fontWeight: '900',
|
| 175 |
+
color: COLORS.primaryDark,
|
| 176 |
+
marginBottom: 2,
|
| 177 |
+
},
|
| 178 |
+
tileSubLabel: {
|
| 179 |
+
fontSize: 13,
|
| 180 |
+
color: COLORS.textLight,
|
| 181 |
+
fontWeight: '600',
|
| 182 |
+
},
|
| 183 |
+
arrowContainer: {
|
| 184 |
+
width: 40,
|
| 185 |
+
height: 40,
|
| 186 |
+
borderRadius: 20,
|
| 187 |
+
backgroundColor: COLORS.primary,
|
| 188 |
+
justifyContent: 'center',
|
| 189 |
alignItems: 'center',
|
| 190 |
},
|
| 191 |
+
arrowIcon: {
|
| 192 |
+
color: COLORS.white,
|
| 193 |
fontSize: 20,
|
| 194 |
+
fontWeight: '900',
|
| 195 |
+
},
|
| 196 |
+
footer: {
|
| 197 |
+
marginTop: 60,
|
| 198 |
+
alignItems: 'center',
|
| 199 |
+
},
|
| 200 |
+
divider: {
|
| 201 |
+
width: 40,
|
| 202 |
+
height: 2,
|
| 203 |
+
backgroundColor: COLORS.border,
|
| 204 |
+
marginVertical: 12,
|
| 205 |
+
},
|
| 206 |
+
footerText: {
|
| 207 |
+
fontSize: 12,
|
| 208 |
+
color: COLORS.textLight,
|
| 209 |
fontWeight: 'bold',
|
| 210 |
+
opacity: 0.6,
|
|
|
|
| 211 |
},
|
| 212 |
+
powerText: {
|
| 213 |
+
fontSize: 10,
|
| 214 |
+
color: COLORS.textLight,
|
| 215 |
+
letterSpacing: 1.5,
|
| 216 |
+
textTransform: 'uppercase',
|
| 217 |
+
opacity: 0.5,
|
| 218 |
+
}
|
| 219 |
});
|
| 220 |
|
| 221 |
export default LanguageScreen;
|
screens/ResultScreen.js
CHANGED
|
@@ -9,24 +9,64 @@ const ResultScreen = ({ route, navigation }) => {
|
|
| 9 |
// We expect imageUri and language from HomeScreen
|
| 10 |
const { imageUri, language, analysisResult: initialResult } = route.params || {};
|
| 11 |
|
| 12 |
-
const [loading, setLoading] = useState(
|
| 13 |
const [analysisResult, setAnalysisResult] = useState(initialResult || null);
|
|
|
|
| 14 |
const [activeTab, setActiveTab] = useState('organic');
|
| 15 |
|
| 16 |
const translations = TRANSLATIONS[language] || TRANSLATIONS['en'];
|
| 17 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
useEffect(() => {
|
| 19 |
-
if (!initialResult && imageUri) {
|
| 20 |
performAnalysis();
|
| 21 |
}
|
| 22 |
-
}, [imageUri, initialResult]);
|
| 23 |
|
| 24 |
const performAnalysis = async () => {
|
|
|
|
| 25 |
try {
|
| 26 |
const result = await analyzeImage(imageUri);
|
| 27 |
setAnalysisResult(result);
|
| 28 |
|
| 29 |
-
// Save to history
|
| 30 |
const scanData = {
|
| 31 |
id: Date.now().toString(),
|
| 32 |
timestamp: Date.now(),
|
|
@@ -42,11 +82,45 @@ const ResultScreen = ({ route, navigation }) => {
|
|
| 42 |
}
|
| 43 |
};
|
| 44 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
if (loading) {
|
| 46 |
return (
|
| 47 |
-
<View style={styles.
|
| 48 |
<ActivityIndicator size="large" color={COLORS.primary} />
|
| 49 |
-
<Text style={styles.
|
| 50 |
</View>
|
| 51 |
);
|
| 52 |
}
|
|
@@ -54,10 +128,10 @@ const ResultScreen = ({ route, navigation }) => {
|
|
| 54 |
if (!analysisResult) {
|
| 55 |
return (
|
| 56 |
<View style={styles.container}>
|
| 57 |
-
<View style={styles.
|
| 58 |
-
<Text style={
|
| 59 |
-
<TouchableOpacity style={styles.
|
| 60 |
-
<Text style={styles.
|
| 61 |
</TouchableOpacity>
|
| 62 |
</View>
|
| 63 |
</View>
|
|
@@ -67,123 +141,110 @@ const ResultScreen = ({ route, navigation }) => {
|
|
| 67 |
const { crop, name, isHealthy, severity, symptoms, cause, remedy, remedy_organic, remedy_chemical, prevention, confidence } = analysisResult;
|
| 68 |
|
| 69 |
const getSeverityColor = (sev) => {
|
| 70 |
-
if (!sev) return COLORS.
|
| 71 |
switch (sev.toLowerCase()) {
|
| 72 |
-
case 'low': return COLORS.
|
| 73 |
-
case 'medium': return
|
| 74 |
case 'high': return COLORS.error;
|
| 75 |
-
default: return COLORS.
|
| 76 |
}
|
| 77 |
};
|
| 78 |
|
| 79 |
-
const renderHeader = () => (
|
| 80 |
-
<View style={[styles.header, { backgroundColor: isHealthy ? COLORS.secondary : COLORS.error }]}>
|
| 81 |
-
<TouchableOpacity onPress={() => navigation.goBack()} style={styles.backButton}>
|
| 82 |
-
<Text style={styles.backButtonText}>←</Text>
|
| 83 |
-
</TouchableOpacity>
|
| 84 |
-
<View style={styles.logoCircleSmall}>
|
| 85 |
-
<Image
|
| 86 |
-
source={require('../assets/logo.png')}
|
| 87 |
-
style={styles.headerLogoSmall}
|
| 88 |
-
resizeMode="cover"
|
| 89 |
-
/>
|
| 90 |
-
</View>
|
| 91 |
-
<Text style={[styles.headerTitle, { marginLeft: 10 }]}>{translations.results}</Text>
|
| 92 |
-
</View>
|
| 93 |
-
);
|
| 94 |
-
|
| 95 |
-
const renderTab = (key, label) => (
|
| 96 |
-
<TouchableOpacity
|
| 97 |
-
style={[styles.tab, activeTab === key && styles.activeTab]}
|
| 98 |
-
onPress={() => setActiveTab(key)}
|
| 99 |
-
>
|
| 100 |
-
<Text style={[styles.tabText, activeTab === key && styles.activeTabText]}>{label}</Text>
|
| 101 |
-
</TouchableOpacity>
|
| 102 |
-
);
|
| 103 |
-
|
| 104 |
return (
|
| 105 |
<View style={styles.container}>
|
| 106 |
-
{renderHeader()}
|
| 107 |
|
| 108 |
-
<ScrollView showsVerticalScrollIndicator={false}>
|
| 109 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
|
| 111 |
-
<View style={styles.
|
| 112 |
-
<View style={styles.
|
| 113 |
-
<Text style={styles.
|
| 114 |
-
<
|
| 115 |
-
<View style={styles.confidenceBadge}>
|
| 116 |
-
<Text style={styles.confidenceText}>{confidence}%</Text>
|
| 117 |
-
</View>
|
| 118 |
-
{analysisResult.isMock && (
|
| 119 |
-
<Text style={{ fontSize: 10, color: COLORS.gray, marginTop: 2 }}>
|
| 120 |
-
Offline Mode
|
| 121 |
-
</Text>
|
| 122 |
-
)}
|
| 123 |
-
</View>
|
| 124 |
</View>
|
| 125 |
|
| 126 |
{!isHealthy && (
|
| 127 |
-
<View style={styles.
|
| 128 |
-
<View style={[styles.
|
| 129 |
-
<
|
|
|
|
|
|
|
|
|
|
| 130 |
</View>
|
| 131 |
-
<Text style={styles.cropText}>{translations.detected}: {crop}</Text>
|
| 132 |
</View>
|
| 133 |
)}
|
| 134 |
|
| 135 |
-
<
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
|
|
|
|
|
|
| 139 |
|
| 140 |
{!isHealthy && cause && (
|
| 141 |
-
<>
|
| 142 |
-
<Text style={styles.
|
| 143 |
-
<Text style={styles.
|
| 144 |
-
|
| 145 |
-
<View style={styles.tabContainer}>
|
| 146 |
-
{renderTab('organic', analysisResult.id === 'UNKNOWN' ? translations.unknownInstruction : translations.organic)}
|
| 147 |
-
{renderTab('chemical', analysisResult.id === 'UNKNOWN' ? translations.unknownAdvice : translations.chemical)}
|
| 148 |
-
{renderTab('prevention', analysisResult.id === 'UNKNOWN' ? translations.unknownNextSteps : translations.prevention)}
|
| 149 |
-
</View>
|
| 150 |
-
|
| 151 |
-
<View style={styles.remedyBox}>
|
| 152 |
-
{activeTab === 'organic' && <Text style={styles.remedyText}>{remedy_organic[language]}</Text>}
|
| 153 |
-
{activeTab === 'chemical' && <Text style={styles.remedyText}>{remedy_chemical[language]}</Text>}
|
| 154 |
-
{activeTab === 'prevention' && <Text style={styles.remedyText}>{prevention[language]}</Text>}
|
| 155 |
-
</View>
|
| 156 |
-
</>
|
| 157 |
)}
|
| 158 |
|
| 159 |
{isHealthy && remedy && (
|
| 160 |
-
<>
|
| 161 |
-
<Text style={styles.
|
| 162 |
-
<Text style={styles.
|
| 163 |
-
</>
|
| 164 |
)}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 165 |
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
|
|
|
| 169 |
{analysisResult.diagnosticChecklist.title[language]}
|
| 170 |
</Text>
|
| 171 |
-
{analysisResult.diagnosticChecklist.steps.map((step, index) => (
|
| 172 |
-
<View key={index} style={styles.checkItem}>
|
| 173 |
-
<View style={styles.bullet} />
|
| 174 |
-
<Text style={styles.checkText}>{step[language]}</Text>
|
| 175 |
-
</View>
|
| 176 |
-
))}
|
| 177 |
</View>
|
| 178 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 179 |
|
| 180 |
-
<
|
| 181 |
-
<View style={{ height: 30 }} />
|
| 182 |
</ScrollView>
|
| 183 |
|
| 184 |
-
<
|
| 185 |
-
<
|
| 186 |
-
|
|
|
|
|
|
|
| 187 |
</View>
|
| 188 |
);
|
| 189 |
};
|
|
@@ -195,202 +256,371 @@ const styles = StyleSheet.create({
|
|
| 195 |
},
|
| 196 |
header: {
|
| 197 |
paddingTop: 50,
|
| 198 |
-
paddingBottom:
|
| 199 |
-
paddingHorizontal:
|
| 200 |
flexDirection: 'row',
|
| 201 |
alignItems: 'center',
|
| 202 |
-
borderBottomLeftRadius:
|
| 203 |
-
borderBottomRightRadius:
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 207 |
},
|
| 208 |
-
|
| 209 |
fontSize: 24,
|
| 210 |
-
color:
|
| 211 |
fontWeight: 'bold',
|
| 212 |
},
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 219 |
justifyContent: 'center',
|
| 220 |
alignItems: 'center',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 221 |
},
|
| 222 |
-
|
| 223 |
width: '100%',
|
| 224 |
height: '100%',
|
| 225 |
},
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 230 |
},
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
borderRadius: 20,
|
| 236 |
-
marginTop: 20,
|
| 237 |
-
marginBottom: 20,
|
| 238 |
},
|
| 239 |
-
|
| 240 |
-
|
|
|
|
|
|
|
|
|
|
| 241 |
},
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
marginBottom:
|
|
|
|
|
|
|
|
|
|
| 247 |
},
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
fontWeight: 'bold',
|
| 251 |
-
color: COLORS.text,
|
| 252 |
-
flex: 1,
|
| 253 |
},
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 259 |
},
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
fontWeight: 'bold',
|
| 263 |
},
|
| 264 |
-
|
| 265 |
flexDirection: 'row',
|
| 266 |
alignItems: 'center',
|
| 267 |
-
|
|
|
|
|
|
|
|
|
|
| 268 |
},
|
| 269 |
-
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
borderRadius:
|
| 273 |
-
marginRight:
|
| 274 |
},
|
| 275 |
-
|
| 276 |
-
color: '#fff',
|
| 277 |
-
fontWeight: 'bold',
|
| 278 |
fontSize: 12,
|
|
|
|
|
|
|
| 279 |
},
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
color: COLORS.gray,
|
| 283 |
},
|
| 284 |
-
|
| 285 |
-
fontSize:
|
| 286 |
-
fontWeight: '
|
| 287 |
color: COLORS.primary,
|
| 288 |
-
|
| 289 |
-
marginBottom:
|
|
|
|
| 290 |
},
|
| 291 |
-
|
| 292 |
fontSize: 16,
|
| 293 |
color: COLORS.text,
|
| 294 |
lineHeight: 24,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 295 |
},
|
| 296 |
-
|
| 297 |
flexDirection: 'row',
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
},
|
| 303 |
-
|
| 304 |
flex: 1,
|
| 305 |
-
paddingVertical:
|
| 306 |
alignItems: 'center',
|
| 307 |
-
borderRadius:
|
| 308 |
},
|
| 309 |
-
|
| 310 |
-
backgroundColor:
|
| 311 |
-
|
| 312 |
-
shadowOffset: { width: 0, height: 1 },
|
| 313 |
-
shadowOpacity: 0.1,
|
| 314 |
-
shadowRadius: 1,
|
| 315 |
-
elevation: 1,
|
| 316 |
},
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
fontWeight: '
|
|
|
|
| 320 |
},
|
| 321 |
-
|
| 322 |
-
color: COLORS.
|
|
|
|
| 323 |
},
|
| 324 |
-
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
borderLeftWidth: 4,
|
| 330 |
borderLeftColor: COLORS.primary,
|
|
|
|
| 331 |
},
|
| 332 |
-
|
| 333 |
-
fontSize:
|
| 334 |
color: COLORS.text,
|
| 335 |
lineHeight: 24,
|
|
|
|
| 336 |
},
|
| 337 |
-
|
| 338 |
-
backgroundColor: COLORS.
|
| 339 |
-
|
| 340 |
-
padding:
|
| 341 |
-
|
| 342 |
-
alignItems: 'center',
|
| 343 |
-
},
|
| 344 |
-
homeButtonText: {
|
| 345 |
-
color: '#fff',
|
| 346 |
-
fontSize: 18,
|
| 347 |
-
fontWeight: 'bold',
|
| 348 |
},
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
|
| 352 |
-
|
| 353 |
-
|
| 354 |
-
borderWidth: 1,
|
| 355 |
-
borderColor: COLORS.primary + '30',
|
| 356 |
-
marginBottom: 10,
|
| 357 |
},
|
| 358 |
-
|
| 359 |
fontSize: 18,
|
| 360 |
-
fontWeight: '
|
| 361 |
-
color: COLORS.
|
| 362 |
-
marginBottom: 15,
|
| 363 |
},
|
| 364 |
-
|
| 365 |
flexDirection: 'row',
|
| 366 |
alignItems: 'flex-start',
|
| 367 |
-
marginBottom:
|
| 368 |
},
|
| 369 |
-
|
| 370 |
-
width:
|
| 371 |
-
height:
|
| 372 |
-
borderRadius:
|
| 373 |
-
backgroundColor: COLORS.
|
| 374 |
-
marginTop:
|
| 375 |
-
marginRight:
|
| 376 |
},
|
| 377 |
-
|
| 378 |
fontSize: 15,
|
| 379 |
-
color:
|
| 380 |
lineHeight: 22,
|
| 381 |
flex: 1,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 382 |
},
|
| 383 |
-
|
| 384 |
flex: 1,
|
| 385 |
justifyContent: 'center',
|
| 386 |
alignItems: 'center',
|
| 387 |
-
backgroundColor: COLORS.background
|
| 388 |
},
|
| 389 |
-
|
| 390 |
marginTop: 20,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 391 |
fontSize: 18,
|
| 392 |
-
|
| 393 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 394 |
}
|
| 395 |
});
|
| 396 |
|
|
|
|
| 9 |
// We expect imageUri and language from HomeScreen
|
| 10 |
const { imageUri, language, analysisResult: initialResult } = route.params || {};
|
| 11 |
|
| 12 |
+
const [loading, setLoading] = useState(false);
|
| 13 |
const [analysisResult, setAnalysisResult] = useState(initialResult || null);
|
| 14 |
+
const [confirmed, setConfirmed] = useState(!!initialResult);
|
| 15 |
const [activeTab, setActiveTab] = useState('organic');
|
| 16 |
|
| 17 |
const translations = TRANSLATIONS[language] || TRANSLATIONS['en'];
|
| 18 |
|
| 19 |
+
const renderHeader = (isHealthyResult = null) => {
|
| 20 |
+
const headerBg = isHealthyResult === null
|
| 21 |
+
? COLORS.primary
|
| 22 |
+
: (isHealthyResult ? COLORS.success : COLORS.error);
|
| 23 |
+
|
| 24 |
+
return (
|
| 25 |
+
<View style={[styles.header, { backgroundColor: headerBg }]}>
|
| 26 |
+
<TouchableOpacity onPress={() => navigation.goBack()} style={styles.headerButton}>
|
| 27 |
+
<Text style={styles.headerButtonText}>←</Text>
|
| 28 |
+
</TouchableOpacity>
|
| 29 |
+
<View style={styles.headerTitleUnit}>
|
| 30 |
+
<Text style={styles.headerLabel}>{translations.results}</Text>
|
| 31 |
+
{isHealthyResult !== null && (
|
| 32 |
+
<Text style={styles.headerStatusSub}>
|
| 33 |
+
{isHealthyResult ? "PLANT IS HEALTHY" : "ISSUE DETECTED"}
|
| 34 |
+
</Text>
|
| 35 |
+
)}
|
| 36 |
+
</View>
|
| 37 |
+
<View style={styles.logoCircleHeader}>
|
| 38 |
+
<Image
|
| 39 |
+
source={require('../assets/logo.png')}
|
| 40 |
+
style={styles.logoImgSmall}
|
| 41 |
+
resizeMode="contain"
|
| 42 |
+
/>
|
| 43 |
+
</View>
|
| 44 |
+
</View>
|
| 45 |
+
);
|
| 46 |
+
};
|
| 47 |
+
|
| 48 |
+
const renderTab = (key, label) => (
|
| 49 |
+
<TouchableOpacity
|
| 50 |
+
style={[styles.tabItem, activeTab === key && styles.activeTabItem]}
|
| 51 |
+
onPress={() => setActiveTab(key)}
|
| 52 |
+
activeOpacity={0.7}
|
| 53 |
+
>
|
| 54 |
+
<Text style={[styles.tabLabelText, activeTab === key && styles.activeTabLabelText]}>{label}</Text>
|
| 55 |
+
</TouchableOpacity>
|
| 56 |
+
);
|
| 57 |
+
|
| 58 |
useEffect(() => {
|
| 59 |
+
if (!initialResult && imageUri && confirmed) {
|
| 60 |
performAnalysis();
|
| 61 |
}
|
| 62 |
+
}, [imageUri, initialResult, confirmed]);
|
| 63 |
|
| 64 |
const performAnalysis = async () => {
|
| 65 |
+
setLoading(true);
|
| 66 |
try {
|
| 67 |
const result = await analyzeImage(imageUri);
|
| 68 |
setAnalysisResult(result);
|
| 69 |
|
|
|
|
| 70 |
const scanData = {
|
| 71 |
id: Date.now().toString(),
|
| 72 |
timestamp: Date.now(),
|
|
|
|
| 82 |
}
|
| 83 |
};
|
| 84 |
|
| 85 |
+
if (!confirmed) {
|
| 86 |
+
return (
|
| 87 |
+
<View style={styles.container}>
|
| 88 |
+
{renderHeader()}
|
| 89 |
+
<View style={styles.confirmBox}>
|
| 90 |
+
<View style={styles.imageCard}>
|
| 91 |
+
<Image source={{ uri: imageUri }} style={styles.previewFullScreen} />
|
| 92 |
+
<View style={styles.imageOverlay}>
|
| 93 |
+
<Text style={styles.overlayText}>READY FOR AI ANALYSIS</Text>
|
| 94 |
+
</View>
|
| 95 |
+
</View>
|
| 96 |
+
<View style={styles.confirmActions}>
|
| 97 |
+
<Text style={styles.confirmMainTitle}>{translations.analyseNow}</Text>
|
| 98 |
+
<Text style={styles.confirmSubTitle}>
|
| 99 |
+
{language === 'ta' ? "இந்த படத்தை ஆய்வு செய்ய விரும்புகிறீர்களா?" : "Our AI will now check this leaf for 30+ potential issues."}
|
| 100 |
+
</Text>
|
| 101 |
+
<TouchableOpacity
|
| 102 |
+
style={styles.primaryAnalyseBtn}
|
| 103 |
+
onPress={() => setConfirmed(true)}
|
| 104 |
+
>
|
| 105 |
+
<Text style={styles.primaryAnalyseBtnText}>START ANALYSIS</Text>
|
| 106 |
+
</TouchableOpacity>
|
| 107 |
+
<TouchableOpacity
|
| 108 |
+
style={styles.cancelAction}
|
| 109 |
+
onPress={() => navigation.goBack()}
|
| 110 |
+
>
|
| 111 |
+
<Text style={styles.cancelActionText}>{translations.cancel}</Text>
|
| 112 |
+
</TouchableOpacity>
|
| 113 |
+
</View>
|
| 114 |
+
</View>
|
| 115 |
+
</View>
|
| 116 |
+
);
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
if (loading) {
|
| 120 |
return (
|
| 121 |
+
<View style={styles.loaderContainer}>
|
| 122 |
<ActivityIndicator size="large" color={COLORS.primary} />
|
| 123 |
+
<Text style={styles.loaderText}>{translations.analyzing || "Consulting AI Expert..."}</Text>
|
| 124 |
</View>
|
| 125 |
);
|
| 126 |
}
|
|
|
|
| 128 |
if (!analysisResult) {
|
| 129 |
return (
|
| 130 |
<View style={styles.container}>
|
| 131 |
+
<View style={styles.loaderContainer}>
|
| 132 |
+
<Text style={styles.errorTextHeading}>{translations.error || "System Error"}</Text>
|
| 133 |
+
<TouchableOpacity style={styles.errorBtn} onPress={() => navigation.navigate('Home')}>
|
| 134 |
+
<Text style={styles.errorBtnText}>{translations.backHome}</Text>
|
| 135 |
</TouchableOpacity>
|
| 136 |
</View>
|
| 137 |
</View>
|
|
|
|
| 141 |
const { crop, name, isHealthy, severity, symptoms, cause, remedy, remedy_organic, remedy_chemical, prevention, confidence } = analysisResult;
|
| 142 |
|
| 143 |
const getSeverityColor = (sev) => {
|
| 144 |
+
if (!sev) return COLORS.textLight;
|
| 145 |
switch (sev.toLowerCase()) {
|
| 146 |
+
case 'low': return COLORS.success;
|
| 147 |
+
case 'medium': return COLORS.warning;
|
| 148 |
case 'high': return COLORS.error;
|
| 149 |
+
default: return COLORS.textLight;
|
| 150 |
}
|
| 151 |
};
|
| 152 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 153 |
return (
|
| 154 |
<View style={styles.container}>
|
| 155 |
+
{renderHeader(isHealthy)}
|
| 156 |
|
| 157 |
+
<ScrollView showsVerticalScrollIndicator={false} contentContainerStyle={styles.scrollBody}>
|
| 158 |
+
<View style={styles.imageFrame}>
|
| 159 |
+
<Image source={{ uri: imageUri }} style={styles.mainResultImage} />
|
| 160 |
+
<View style={styles.confidenceFlag}>
|
| 161 |
+
<Text style={styles.flagValue}>{confidence}%</Text>
|
| 162 |
+
<Text style={styles.flagLabel}>CONFIDENCE</Text>
|
| 163 |
+
</View>
|
| 164 |
+
</View>
|
| 165 |
|
| 166 |
+
<View style={styles.diagnosisCard}>
|
| 167 |
+
<View style={styles.diagnosisHead}>
|
| 168 |
+
<Text style={styles.cropLabelText}>{crop.toUpperCase()}</Text>
|
| 169 |
+
<Text style={styles.diagnosisMainTitle}>{name[language]}</Text>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 170 |
</View>
|
| 171 |
|
| 172 |
{!isHealthy && (
|
| 173 |
+
<View style={styles.severityRow}>
|
| 174 |
+
<View style={[styles.severityPill, { backgroundColor: getSeverityColor(severity) + '20' }]}>
|
| 175 |
+
<View style={[styles.severityDot, { backgroundColor: getSeverityColor(severity) }]} />
|
| 176 |
+
<Text style={[styles.severityPillText, { color: getSeverityColor(severity) }]}>
|
| 177 |
+
{translations.severity}: {severity.toUpperCase()}
|
| 178 |
+
</Text>
|
| 179 |
</View>
|
|
|
|
| 180 |
</View>
|
| 181 |
)}
|
| 182 |
|
| 183 |
+
<View style={styles.infoSection}>
|
| 184 |
+
<Text style={styles.sectionHeaderLabel}>
|
| 185 |
+
{analysisResult.id === 'UNKNOWN' ? translations.unknownStatus : translations.symptoms}
|
| 186 |
+
</Text>
|
| 187 |
+
<Text style={styles.sectionDescText}>{symptoms[language]}</Text>
|
| 188 |
+
</View>
|
| 189 |
|
| 190 |
{!isHealthy && cause && (
|
| 191 |
+
<View style={styles.infoSection}>
|
| 192 |
+
<Text style={styles.sectionHeaderLabel}>{translations.cause}</Text>
|
| 193 |
+
<Text style={styles.sectionDescText}>{cause[language]}</Text>
|
| 194 |
+
</View>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 195 |
)}
|
| 196 |
|
| 197 |
{isHealthy && remedy && (
|
| 198 |
+
<View style={styles.infoSection}>
|
| 199 |
+
<Text style={styles.sectionHeaderLabel}>{translations.remedy}</Text>
|
| 200 |
+
<Text style={styles.sectionDescText}>{remedy[language]}</Text>
|
| 201 |
+
</View>
|
| 202 |
)}
|
| 203 |
+
</View>
|
| 204 |
+
|
| 205 |
+
{!isHealthy && (
|
| 206 |
+
<View style={styles.treatmentHub}>
|
| 207 |
+
<Text style={styles.hubTitle}>TREATMENT OPTIONS</Text>
|
| 208 |
+
<View style={styles.tabSelector}>
|
| 209 |
+
{renderTab('organic', analysisResult.id === 'UNKNOWN' ? translations.unknownInstruction : translations.organic)}
|
| 210 |
+
{renderTab('chemical', analysisResult.id === 'UNKNOWN' ? translations.unknownAdvice : translations.chemical)}
|
| 211 |
+
{renderTab('prevention', analysisResult.id === 'UNKNOWN' ? translations.unknownNextSteps : translations.prevention)}
|
| 212 |
+
</View>
|
| 213 |
+
|
| 214 |
+
<View style={styles.remedyDisplay}>
|
| 215 |
+
<Text style={styles.remedyContentText}>
|
| 216 |
+
{activeTab === 'organic' && remedy_organic[language]}
|
| 217 |
+
{activeTab === 'chemical' && remedy_chemical[language]}
|
| 218 |
+
{activeTab === 'prevention' && prevention[language]}
|
| 219 |
+
</Text>
|
| 220 |
+
</View>
|
| 221 |
+
</View>
|
| 222 |
+
)}
|
| 223 |
|
| 224 |
+
{analysisResult.diagnosticChecklist && (
|
| 225 |
+
<View style={styles.checklistBoard}>
|
| 226 |
+
<View style={styles.checklistHead}>
|
| 227 |
+
<Text style={styles.checklistHeadTitle}>
|
| 228 |
{analysisResult.diagnosticChecklist.title[language]}
|
| 229 |
</Text>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 230 |
</View>
|
| 231 |
+
{analysisResult.diagnosticChecklist.steps.map((step, index) => (
|
| 232 |
+
<View key={index} style={styles.checklistLine}>
|
| 233 |
+
<View style={styles.checkBullet} />
|
| 234 |
+
<Text style={styles.checklistLineText}>{step[language]}</Text>
|
| 235 |
+
</View>
|
| 236 |
+
))}
|
| 237 |
+
</View>
|
| 238 |
+
)}
|
| 239 |
|
| 240 |
+
<View style={{ height: 40 }} />
|
|
|
|
| 241 |
</ScrollView>
|
| 242 |
|
| 243 |
+
<View style={styles.bottomActions}>
|
| 244 |
+
<TouchableOpacity style={styles.returnHomeBtn} onPress={() => navigation.navigate('Home')}>
|
| 245 |
+
<Text style={styles.returnHomeBtnText}>{translations.backHome}</Text>
|
| 246 |
+
</TouchableOpacity>
|
| 247 |
+
</View>
|
| 248 |
</View>
|
| 249 |
);
|
| 250 |
};
|
|
|
|
| 256 |
},
|
| 257 |
header: {
|
| 258 |
paddingTop: 50,
|
| 259 |
+
paddingBottom: 25,
|
| 260 |
+
paddingHorizontal: SIZES.padding,
|
| 261 |
flexDirection: 'row',
|
| 262 |
alignItems: 'center',
|
| 263 |
+
borderBottomLeftRadius: 35,
|
| 264 |
+
borderBottomRightRadius: 35,
|
| 265 |
+
...COLORS.shadow.lg,
|
| 266 |
+
},
|
| 267 |
+
headerButton: {
|
| 268 |
+
width: 44,
|
| 269 |
+
height: 44,
|
| 270 |
+
borderRadius: 22,
|
| 271 |
+
backgroundColor: 'rgba(255,255,255,0.2)',
|
| 272 |
+
justifyContent: 'center',
|
| 273 |
+
alignItems: 'center',
|
| 274 |
},
|
| 275 |
+
headerButtonText: {
|
| 276 |
fontSize: 24,
|
| 277 |
+
color: COLORS.white,
|
| 278 |
fontWeight: 'bold',
|
| 279 |
},
|
| 280 |
+
headerTitleUnit: {
|
| 281 |
+
flex: 1,
|
| 282 |
+
marginLeft: 15,
|
| 283 |
+
},
|
| 284 |
+
headerLabel: {
|
| 285 |
+
fontSize: 20,
|
| 286 |
+
fontWeight: '900',
|
| 287 |
+
color: COLORS.white,
|
| 288 |
+
letterSpacing: 0.5,
|
| 289 |
+
},
|
| 290 |
+
headerStatusSub: {
|
| 291 |
+
fontSize: 10,
|
| 292 |
+
fontWeight: '800',
|
| 293 |
+
color: 'rgba(255,255,255,0.8)',
|
| 294 |
+
letterSpacing: 1,
|
| 295 |
+
marginTop: 2,
|
| 296 |
+
},
|
| 297 |
+
logoCircleHeader: {
|
| 298 |
+
width: 48,
|
| 299 |
+
height: 48,
|
| 300 |
+
backgroundColor: COLORS.white,
|
| 301 |
+
borderRadius: 24,
|
| 302 |
justifyContent: 'center',
|
| 303 |
alignItems: 'center',
|
| 304 |
+
...COLORS.shadow.sm,
|
| 305 |
+
},
|
| 306 |
+
logoImgSmall: {
|
| 307 |
+
width: '70%',
|
| 308 |
+
height: '70%',
|
| 309 |
+
},
|
| 310 |
+
scrollBody: {
|
| 311 |
+
padding: SIZES.padding,
|
| 312 |
+
},
|
| 313 |
+
imageFrame: {
|
| 314 |
+
width: '100%',
|
| 315 |
+
height: 280,
|
| 316 |
+
borderRadius: 30,
|
| 317 |
+
backgroundColor: COLORS.surface,
|
| 318 |
+
...COLORS.shadow.md,
|
| 319 |
+
overflow: 'hidden',
|
| 320 |
+
marginBottom: 25,
|
| 321 |
},
|
| 322 |
+
mainResultImage: {
|
| 323 |
width: '100%',
|
| 324 |
height: '100%',
|
| 325 |
},
|
| 326 |
+
confidenceFlag: {
|
| 327 |
+
position: 'absolute',
|
| 328 |
+
bottom: 20,
|
| 329 |
+
right: 20,
|
| 330 |
+
backgroundColor: COLORS.surface,
|
| 331 |
+
paddingHorizontal: 12,
|
| 332 |
+
paddingVertical: 8,
|
| 333 |
+
borderRadius: 15,
|
| 334 |
+
alignItems: 'center',
|
| 335 |
+
borderWidth: 1,
|
| 336 |
+
borderColor: COLORS.border,
|
| 337 |
+
...COLORS.shadow.sm,
|
| 338 |
},
|
| 339 |
+
flagValue: {
|
| 340 |
+
fontSize: 18,
|
| 341 |
+
fontWeight: '900',
|
| 342 |
+
color: COLORS.primary,
|
|
|
|
|
|
|
|
|
|
| 343 |
},
|
| 344 |
+
flagLabel: {
|
| 345 |
+
fontSize: 8,
|
| 346 |
+
fontWeight: '800',
|
| 347 |
+
color: COLORS.textLight,
|
| 348 |
+
letterSpacing: 0.5,
|
| 349 |
},
|
| 350 |
+
diagnosisCard: {
|
| 351 |
+
backgroundColor: COLORS.surface,
|
| 352 |
+
borderRadius: 30,
|
| 353 |
+
padding: 25,
|
| 354 |
+
marginBottom: 20,
|
| 355 |
+
...COLORS.shadow.md,
|
| 356 |
+
borderWidth: 1,
|
| 357 |
+
borderColor: COLORS.border,
|
| 358 |
},
|
| 359 |
+
diagnosisHead: {
|
| 360 |
+
marginBottom: 15,
|
|
|
|
|
|
|
|
|
|
| 361 |
},
|
| 362 |
+
cropLabelText: {
|
| 363 |
+
fontSize: 12,
|
| 364 |
+
fontWeight: '900',
|
| 365 |
+
color: COLORS.textLight,
|
| 366 |
+
letterSpacing: 1.5,
|
| 367 |
+
marginBottom: 4,
|
| 368 |
+
},
|
| 369 |
+
diagnosisMainTitle: {
|
| 370 |
+
fontSize: 26,
|
| 371 |
+
fontWeight: '900',
|
| 372 |
+
color: COLORS.text,
|
| 373 |
},
|
| 374 |
+
severityRow: {
|
| 375 |
+
marginBottom: 20,
|
|
|
|
| 376 |
},
|
| 377 |
+
severityPill: {
|
| 378 |
flexDirection: 'row',
|
| 379 |
alignItems: 'center',
|
| 380 |
+
alignSelf: 'flex-start',
|
| 381 |
+
paddingHorizontal: 12,
|
| 382 |
+
paddingVertical: 6,
|
| 383 |
+
borderRadius: 12,
|
| 384 |
},
|
| 385 |
+
severityDot: {
|
| 386 |
+
width: 8,
|
| 387 |
+
height: 8,
|
| 388 |
+
borderRadius: 4,
|
| 389 |
+
marginRight: 8,
|
| 390 |
},
|
| 391 |
+
severityPillText: {
|
|
|
|
|
|
|
| 392 |
fontSize: 12,
|
| 393 |
+
fontWeight: '900',
|
| 394 |
+
letterSpacing: 0.5,
|
| 395 |
},
|
| 396 |
+
infoSection: {
|
| 397 |
+
marginBottom: 20,
|
|
|
|
| 398 |
},
|
| 399 |
+
sectionHeaderLabel: {
|
| 400 |
+
fontSize: 14,
|
| 401 |
+
fontWeight: '900',
|
| 402 |
color: COLORS.primary,
|
| 403 |
+
letterSpacing: 1,
|
| 404 |
+
marginBottom: 8,
|
| 405 |
+
textTransform: 'uppercase',
|
| 406 |
},
|
| 407 |
+
sectionDescText: {
|
| 408 |
fontSize: 16,
|
| 409 |
color: COLORS.text,
|
| 410 |
lineHeight: 24,
|
| 411 |
+
fontWeight: '500',
|
| 412 |
+
},
|
| 413 |
+
treatmentHub: {
|
| 414 |
+
marginBottom: 25,
|
| 415 |
+
},
|
| 416 |
+
hubTitle: {
|
| 417 |
+
fontSize: 14,
|
| 418 |
+
fontWeight: '900',
|
| 419 |
+
color: COLORS.textLight,
|
| 420 |
+
letterSpacing: 1.5,
|
| 421 |
+
marginBottom: 12,
|
| 422 |
+
textAlign: 'center',
|
| 423 |
},
|
| 424 |
+
tabSelector: {
|
| 425 |
flexDirection: 'row',
|
| 426 |
+
backgroundColor: 'rgba(0,0,0,0.05)',
|
| 427 |
+
borderRadius: 20,
|
| 428 |
+
padding: 5,
|
| 429 |
+
marginBottom: 15,
|
| 430 |
},
|
| 431 |
+
tabItem: {
|
| 432 |
flex: 1,
|
| 433 |
+
paddingVertical: 12,
|
| 434 |
alignItems: 'center',
|
| 435 |
+
borderRadius: 15,
|
| 436 |
},
|
| 437 |
+
activeTabItem: {
|
| 438 |
+
backgroundColor: COLORS.surface,
|
| 439 |
+
...COLORS.shadow.sm,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 440 |
},
|
| 441 |
+
tabLabelText: {
|
| 442 |
+
fontSize: 13,
|
| 443 |
+
fontWeight: '700',
|
| 444 |
+
color: COLORS.textLight,
|
| 445 |
},
|
| 446 |
+
activeTabLabelText: {
|
| 447 |
+
color: COLORS.primaryDark,
|
| 448 |
+
fontWeight: '900',
|
| 449 |
},
|
| 450 |
+
remedyDisplay: {
|
| 451 |
+
backgroundColor: COLORS.surface,
|
| 452 |
+
borderRadius: 25,
|
| 453 |
+
padding: 20,
|
| 454 |
+
borderLeftWidth: 6,
|
|
|
|
| 455 |
borderLeftColor: COLORS.primary,
|
| 456 |
+
...COLORS.shadow.sm,
|
| 457 |
},
|
| 458 |
+
remedyContentText: {
|
| 459 |
+
fontSize: 15,
|
| 460 |
color: COLORS.text,
|
| 461 |
lineHeight: 24,
|
| 462 |
+
fontWeight: '500',
|
| 463 |
},
|
| 464 |
+
checklistBoard: {
|
| 465 |
+
backgroundColor: COLORS.primaryDark,
|
| 466 |
+
borderRadius: 30,
|
| 467 |
+
padding: 25,
|
| 468 |
+
...COLORS.shadow.md,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 469 |
},
|
| 470 |
+
checklistHead: {
|
| 471 |
+
marginBottom: 15,
|
| 472 |
+
borderBottomWidth: 1,
|
| 473 |
+
borderBottomColor: 'rgba(255,255,255,0.1)',
|
| 474 |
+
paddingBottom: 10,
|
|
|
|
|
|
|
|
|
|
| 475 |
},
|
| 476 |
+
checklistHeadTitle: {
|
| 477 |
fontSize: 18,
|
| 478 |
+
fontWeight: '900',
|
| 479 |
+
color: COLORS.white,
|
|
|
|
| 480 |
},
|
| 481 |
+
checklistLine: {
|
| 482 |
flexDirection: 'row',
|
| 483 |
alignItems: 'flex-start',
|
| 484 |
+
marginBottom: 15,
|
| 485 |
},
|
| 486 |
+
checkBullet: {
|
| 487 |
+
width: 10,
|
| 488 |
+
height: 10,
|
| 489 |
+
borderRadius: 5,
|
| 490 |
+
backgroundColor: COLORS.secondary,
|
| 491 |
+
marginTop: 6,
|
| 492 |
+
marginRight: 15,
|
| 493 |
},
|
| 494 |
+
checklistLineText: {
|
| 495 |
fontSize: 15,
|
| 496 |
+
color: 'rgba(255,255,255,0.9)',
|
| 497 |
lineHeight: 22,
|
| 498 |
flex: 1,
|
| 499 |
+
fontWeight: '500',
|
| 500 |
+
},
|
| 501 |
+
bottomActions: {
|
| 502 |
+
padding: SIZES.padding,
|
| 503 |
+
backgroundColor: COLORS.surface,
|
| 504 |
+
borderTopWidth: 1,
|
| 505 |
+
borderTopColor: COLORS.border,
|
| 506 |
+
},
|
| 507 |
+
returnHomeBtn: {
|
| 508 |
+
backgroundColor: COLORS.primary,
|
| 509 |
+
paddingVertical: 18,
|
| 510 |
+
borderRadius: 20,
|
| 511 |
+
alignItems: 'center',
|
| 512 |
+
...COLORS.shadow.md,
|
| 513 |
+
},
|
| 514 |
+
returnHomeBtnText: {
|
| 515 |
+
color: COLORS.white,
|
| 516 |
+
fontSize: 18,
|
| 517 |
+
fontWeight: '900',
|
| 518 |
+
letterSpacing: 1,
|
| 519 |
},
|
| 520 |
+
loaderContainer: {
|
| 521 |
flex: 1,
|
| 522 |
justifyContent: 'center',
|
| 523 |
alignItems: 'center',
|
| 524 |
+
backgroundColor: COLORS.background,
|
| 525 |
},
|
| 526 |
+
loaderText: {
|
| 527 |
marginTop: 20,
|
| 528 |
+
fontSize: 16,
|
| 529 |
+
color: COLORS.textLight,
|
| 530 |
+
fontWeight: '700',
|
| 531 |
+
letterSpacing: 0.5,
|
| 532 |
+
},
|
| 533 |
+
confirmBox: {
|
| 534 |
+
flex: 1,
|
| 535 |
+
padding: SIZES.padding,
|
| 536 |
+
},
|
| 537 |
+
imageCard: {
|
| 538 |
+
flex: 1.5,
|
| 539 |
+
borderRadius: 35,
|
| 540 |
+
overflow: 'hidden',
|
| 541 |
+
...COLORS.shadow.lg,
|
| 542 |
+
backgroundColor: COLORS.black,
|
| 543 |
+
},
|
| 544 |
+
previewFullScreen: {
|
| 545 |
+
width: '100%',
|
| 546 |
+
height: '100%',
|
| 547 |
+
opacity: 0.8,
|
| 548 |
+
},
|
| 549 |
+
imageOverlay: {
|
| 550 |
+
position: 'absolute',
|
| 551 |
+
top: 30,
|
| 552 |
+
left: 30,
|
| 553 |
+
backgroundColor: 'rgba(0,0,0,0.5)',
|
| 554 |
+
paddingHorizontal: 12,
|
| 555 |
+
paddingVertical: 6,
|
| 556 |
+
borderRadius: 10,
|
| 557 |
+
borderWidth: 1,
|
| 558 |
+
borderColor: 'rgba(255,255,255,0.3)',
|
| 559 |
+
},
|
| 560 |
+
overlayText: {
|
| 561 |
+
color: COLORS.white,
|
| 562 |
+
fontSize: 10,
|
| 563 |
+
fontWeight: '900',
|
| 564 |
+
letterSpacing: 1,
|
| 565 |
+
},
|
| 566 |
+
confirmActions: {
|
| 567 |
+
flex: 1,
|
| 568 |
+
justifyContent: 'center',
|
| 569 |
+
alignItems: 'center',
|
| 570 |
+
paddingHorizontal: 10,
|
| 571 |
+
},
|
| 572 |
+
confirmMainTitle: {
|
| 573 |
+
fontSize: 28,
|
| 574 |
+
fontWeight: '900',
|
| 575 |
+
color: COLORS.text,
|
| 576 |
+
marginBottom: 10,
|
| 577 |
+
},
|
| 578 |
+
confirmSubTitle: {
|
| 579 |
+
fontSize: 15,
|
| 580 |
+
color: COLORS.textLight,
|
| 581 |
+
textAlign: 'center',
|
| 582 |
+
lineHeight: 22,
|
| 583 |
+
marginBottom: 35,
|
| 584 |
+
},
|
| 585 |
+
primaryAnalyseBtn: {
|
| 586 |
+
backgroundColor: COLORS.primary,
|
| 587 |
+
width: '100%',
|
| 588 |
+
paddingVertical: 20,
|
| 589 |
+
borderRadius: 22,
|
| 590 |
+
alignItems: 'center',
|
| 591 |
+
...COLORS.shadow.lg,
|
| 592 |
+
},
|
| 593 |
+
primaryAnalyseBtnText: {
|
| 594 |
+
color: COLORS.white,
|
| 595 |
fontSize: 18,
|
| 596 |
+
fontWeight: '900',
|
| 597 |
+
letterSpacing: 2,
|
| 598 |
+
},
|
| 599 |
+
cancelAction: {
|
| 600 |
+
marginTop: 20,
|
| 601 |
+
padding: 10,
|
| 602 |
+
},
|
| 603 |
+
cancelActionText: {
|
| 604 |
+
color: COLORS.textLight,
|
| 605 |
+
fontSize: 15,
|
| 606 |
+
fontWeight: '700',
|
| 607 |
+
textDecorationLine: 'underline',
|
| 608 |
+
},
|
| 609 |
+
errorTextHeading: {
|
| 610 |
+
fontSize: 20,
|
| 611 |
+
fontWeight: '900',
|
| 612 |
+
color: COLORS.error,
|
| 613 |
+
marginBottom: 20,
|
| 614 |
+
},
|
| 615 |
+
errorBtn: {
|
| 616 |
+
paddingHorizontal: 30,
|
| 617 |
+
paddingVertical: 15,
|
| 618 |
+
backgroundColor: COLORS.primary,
|
| 619 |
+
borderRadius: 15,
|
| 620 |
+
},
|
| 621 |
+
errorBtnText: {
|
| 622 |
+
color: COLORS.white,
|
| 623 |
+
fontWeight: '800',
|
| 624 |
}
|
| 625 |
});
|
| 626 |
|
screens/ScannerScreen.js
CHANGED
|
@@ -1,45 +1,74 @@
|
|
| 1 |
import React, { useState, useEffect, useRef } from 'react';
|
| 2 |
-
import { StyleSheet, Text, View, TouchableOpacity, Dimensions, Animated, StatusBar, ActivityIndicator } from 'react-native';
|
| 3 |
import { CameraView, useCameraPermissions } from 'expo-camera';
|
| 4 |
import { COLORS, SIZES, FONTS } from '../constants/theme';
|
| 5 |
import { TRANSLATIONS } from '../constants/translations';
|
| 6 |
import { analyzeImage } from '../services/aiService';
|
| 7 |
|
| 8 |
const { width, height } = Dimensions.get('window');
|
|
|
|
| 9 |
|
| 10 |
const ScannerScreen = ({ navigation, route }) => {
|
| 11 |
const language = route.params?.language || 'en';
|
| 12 |
const t = TRANSLATIONS[language] || TRANSLATIONS['en'];
|
| 13 |
|
| 14 |
const [permission, requestPermission] = useCameraPermissions();
|
|
|
|
| 15 |
const [isScanning, setIsScanning] = useState(true);
|
| 16 |
const [loading, setLoading] = useState(false);
|
| 17 |
-
const [
|
| 18 |
|
| 19 |
-
//
|
|
|
|
| 20 |
const pulseAnim = useRef(new Animated.Value(1)).current;
|
| 21 |
const cameraRef = useRef(null);
|
| 22 |
|
| 23 |
useEffect(() => {
|
| 24 |
-
if (isScanning) {
|
| 25 |
-
|
| 26 |
const interval = setInterval(() => {
|
| 27 |
-
if (!loading && isScanning) {
|
| 28 |
autoCapture();
|
| 29 |
}
|
| 30 |
-
},
|
| 31 |
return () => {
|
| 32 |
clearInterval(interval);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
pulseAnim.stopAnimation();
|
| 34 |
};
|
| 35 |
}
|
| 36 |
-
}, [isScanning, loading]);
|
| 37 |
|
| 38 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
Animated.loop(
|
| 40 |
Animated.sequence([
|
| 41 |
Animated.timing(pulseAnim, {
|
| 42 |
-
toValue: 1.
|
| 43 |
duration: 1000,
|
| 44 |
useNativeDriver: true,
|
| 45 |
}),
|
|
@@ -53,44 +82,58 @@ const ScannerScreen = ({ navigation, route }) => {
|
|
| 53 |
};
|
| 54 |
|
| 55 |
const autoCapture = async () => {
|
| 56 |
-
if (!cameraRef.current) return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
|
| 58 |
try {
|
| 59 |
setLoading(true);
|
|
|
|
|
|
|
| 60 |
const photo = await cameraRef.current.takePictureAsync({
|
| 61 |
-
quality: 0.
|
| 62 |
base64: false,
|
| 63 |
});
|
| 64 |
|
| 65 |
const result = await analyzeImage(photo.uri);
|
| 66 |
|
| 67 |
if (result && result.status === 'success') {
|
| 68 |
-
|
| 69 |
-
//
|
| 70 |
-
if (result.confidence > 0.
|
| 71 |
setIsScanning(false);
|
| 72 |
navigation.navigate('Result', {
|
| 73 |
imageUri: photo.uri,
|
| 74 |
analysisResult: result,
|
| 75 |
language
|
| 76 |
});
|
|
|
|
|
|
|
| 77 |
}
|
| 78 |
}
|
| 79 |
} catch (error) {
|
| 80 |
console.log("Scanner Error:", error);
|
|
|
|
| 81 |
} finally {
|
| 82 |
setLoading(false);
|
|
|
|
| 83 |
}
|
| 84 |
};
|
| 85 |
|
| 86 |
-
if (!permission) return <View />;
|
| 87 |
if (!permission.granted) {
|
| 88 |
return (
|
| 89 |
<View style={styles.container}>
|
| 90 |
-
<
|
| 91 |
-
|
| 92 |
-
<Text style={styles.
|
| 93 |
-
|
|
|
|
|
|
|
|
|
|
| 94 |
</View>
|
| 95 |
);
|
| 96 |
}
|
|
@@ -102,56 +145,124 @@ const ScannerScreen = ({ navigation, route }) => {
|
|
| 102 |
style={StyleSheet.absoluteFill}
|
| 103 |
facing="back"
|
| 104 |
ref={cameraRef}
|
|
|
|
| 105 |
/>
|
| 106 |
|
| 107 |
-
{/*
|
| 108 |
-
<View style={styles.
|
| 109 |
-
<View style={styles.
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
<
|
| 113 |
-
|
| 114 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 115 |
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
<View style={styles.
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
<
|
| 125 |
-
</
|
|
|
|
| 126 |
</View>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 127 |
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 133 |
</View>
|
| 134 |
-
|
| 135 |
-
<View style={
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
</View>
|
| 143 |
-
)}
|
| 144 |
</View>
|
| 145 |
-
|
|
|
|
| 146 |
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
|
|
|
| 153 |
</Text>
|
| 154 |
-
</
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 155 |
</View>
|
| 156 |
</View>
|
| 157 |
</View>
|
|
@@ -161,157 +272,264 @@ const ScannerScreen = ({ navigation, route }) => {
|
|
| 161 |
const styles = StyleSheet.create({
|
| 162 |
container: {
|
| 163 |
flex: 1,
|
| 164 |
-
backgroundColor: '
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 165 |
},
|
| 166 |
-
|
| 167 |
flex: 1,
|
| 168 |
-
|
| 169 |
-
|
| 170 |
},
|
| 171 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 172 |
flexDirection: 'row',
|
| 173 |
alignItems: 'center',
|
| 174 |
-
marginTop: 40,
|
| 175 |
},
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
|
|
|
|
|
|
| 180 |
},
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
},
|
| 186 |
-
|
| 187 |
-
color: '
|
| 188 |
-
fontSize:
|
| 189 |
-
fontWeight: '
|
| 190 |
-
|
|
|
|
| 191 |
},
|
| 192 |
-
|
| 193 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 194 |
alignItems: 'center',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 195 |
justifyContent: 'center',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 196 |
},
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
borderWidth: 2,
|
| 201 |
borderRadius: 20,
|
| 202 |
-
|
|
|
|
|
|
|
| 203 |
},
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
borderRightWidth: 6,
|
| 223 |
-
borderColor: COLORS.secondary,
|
| 224 |
-
borderTopRightRadius: 20,
|
| 225 |
-
},
|
| 226 |
-
cornerBottomLeft: {
|
| 227 |
-
position: 'absolute',
|
| 228 |
-
bottom: -2,
|
| 229 |
-
left: -2,
|
| 230 |
-
width: 40,
|
| 231 |
-
height: 40,
|
| 232 |
-
borderBottomWidth: 6,
|
| 233 |
-
borderLeftWidth: 6,
|
| 234 |
-
borderColor: COLORS.secondary,
|
| 235 |
-
borderBottomLeftRadius: 20,
|
| 236 |
-
},
|
| 237 |
-
cornerBottomRight: {
|
| 238 |
-
position: 'absolute',
|
| 239 |
-
bottom: -2,
|
| 240 |
-
right: -2,
|
| 241 |
-
width: 40,
|
| 242 |
-
height: 40,
|
| 243 |
-
borderBottomWidth: 6,
|
| 244 |
-
borderRightWidth: 6,
|
| 245 |
-
borderColor: COLORS.secondary,
|
| 246 |
-
borderBottomRightRadius: 20,
|
| 247 |
-
},
|
| 248 |
-
footer: {
|
| 249 |
-
marginBottom: 40,
|
| 250 |
alignItems: 'center',
|
| 251 |
},
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
padding: 15,
|
| 255 |
-
borderRadius: 15,
|
| 256 |
-
width: '100%',
|
| 257 |
alignItems: 'center',
|
| 258 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 259 |
},
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 263 |
textAlign: 'center',
|
| 264 |
},
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
borderTopColor: 'rgba(255,255,255,0.2)',
|
| 270 |
width: '100%',
|
| 271 |
},
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 277 |
},
|
| 278 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 279 |
alignItems: 'center',
|
| 280 |
-
marginBottom: 20,
|
| 281 |
},
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
|
|
|
|
|
|
| 286 |
},
|
| 287 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 288 |
backgroundColor: COLORS.white,
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
borderRadius: 25,
|
| 292 |
},
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
fontWeight: 'bold',
|
| 296 |
-
fontSize: 14,
|
| 297 |
},
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
|
|
|
|
|
|
| 303 |
},
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
padding: 15,
|
| 307 |
-
borderRadius: 10,
|
| 308 |
-
marginHorizontal: 40,
|
| 309 |
},
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 313 |
textAlign: 'center',
|
|
|
|
|
|
|
| 314 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 315 |
});
|
| 316 |
|
| 317 |
export default ScannerScreen;
|
|
|
|
| 1 |
import React, { useState, useEffect, useRef } from 'react';
|
| 2 |
+
import { StyleSheet, Text, View, TouchableOpacity, Dimensions, Animated, StatusBar, ActivityIndicator, Easing } from 'react-native';
|
| 3 |
import { CameraView, useCameraPermissions } from 'expo-camera';
|
| 4 |
import { COLORS, SIZES, FONTS } from '../constants/theme';
|
| 5 |
import { TRANSLATIONS } from '../constants/translations';
|
| 6 |
import { analyzeImage } from '../services/aiService';
|
| 7 |
|
| 8 |
const { width, height } = Dimensions.get('window');
|
| 9 |
+
const SCAN_SIZE = width * 0.75;
|
| 10 |
|
| 11 |
const ScannerScreen = ({ navigation, route }) => {
|
| 12 |
const language = route.params?.language || 'en';
|
| 13 |
const t = TRANSLATIONS[language] || TRANSLATIONS['en'];
|
| 14 |
|
| 15 |
const [permission, requestPermission] = useCameraPermissions();
|
| 16 |
+
const [scanMode, setScanMode] = useState('auto'); // 'auto' or 'manual'
|
| 17 |
const [isScanning, setIsScanning] = useState(true);
|
| 18 |
const [loading, setLoading] = useState(false);
|
| 19 |
+
const [statusMessage, setStatusMessage] = useState('LOOKING FOR LEAVES...');
|
| 20 |
|
| 21 |
+
// Animations
|
| 22 |
+
const laserAnim = useRef(new Animated.Value(0)).current;
|
| 23 |
const pulseAnim = useRef(new Animated.Value(1)).current;
|
| 24 |
const cameraRef = useRef(null);
|
| 25 |
|
| 26 |
useEffect(() => {
|
| 27 |
+
if (isScanning && scanMode === 'auto') {
|
| 28 |
+
startAnimations();
|
| 29 |
const interval = setInterval(() => {
|
| 30 |
+
if (!loading && isScanning && scanMode === 'auto') {
|
| 31 |
autoCapture();
|
| 32 |
}
|
| 33 |
+
}, 3000);
|
| 34 |
return () => {
|
| 35 |
clearInterval(interval);
|
| 36 |
+
laserAnim.stopAnimation();
|
| 37 |
+
pulseAnim.stopAnimation();
|
| 38 |
+
};
|
| 39 |
+
} else if (isScanning && scanMode === 'manual') {
|
| 40 |
+
startAnimations();
|
| 41 |
+
return () => {
|
| 42 |
+
laserAnim.stopAnimation();
|
| 43 |
pulseAnim.stopAnimation();
|
| 44 |
};
|
| 45 |
}
|
| 46 |
+
}, [isScanning, loading, scanMode]);
|
| 47 |
|
| 48 |
+
const startAnimations = () => {
|
| 49 |
+
// Laser Sweep
|
| 50 |
+
Animated.loop(
|
| 51 |
+
Animated.sequence([
|
| 52 |
+
Animated.timing(laserAnim, {
|
| 53 |
+
toValue: SCAN_SIZE - 4,
|
| 54 |
+
duration: 2000,
|
| 55 |
+
easing: Easing.inOut(Easing.quad),
|
| 56 |
+
useNativeDriver: true,
|
| 57 |
+
}),
|
| 58 |
+
Animated.timing(laserAnim, {
|
| 59 |
+
toValue: 0,
|
| 60 |
+
duration: 2000,
|
| 61 |
+
easing: Easing.inOut(Easing.quad),
|
| 62 |
+
useNativeDriver: true,
|
| 63 |
+
})
|
| 64 |
+
])
|
| 65 |
+
).start();
|
| 66 |
+
|
| 67 |
+
// Frame Pulse
|
| 68 |
Animated.loop(
|
| 69 |
Animated.sequence([
|
| 70 |
Animated.timing(pulseAnim, {
|
| 71 |
+
toValue: 1.05,
|
| 72 |
duration: 1000,
|
| 73 |
useNativeDriver: true,
|
| 74 |
}),
|
|
|
|
| 82 |
};
|
| 83 |
|
| 84 |
const autoCapture = async () => {
|
| 85 |
+
if (!cameraRef.current || loading || !isScanning || scanMode !== 'auto') return;
|
| 86 |
+
performCapture();
|
| 87 |
+
};
|
| 88 |
+
|
| 89 |
+
const performCapture = async () => {
|
| 90 |
+
if (!cameraRef.current || loading) return;
|
| 91 |
|
| 92 |
try {
|
| 93 |
setLoading(true);
|
| 94 |
+
setStatusMessage(scanMode === 'auto' ? 'ANALYZING DETAILS...' : 'CAPTURING...');
|
| 95 |
+
|
| 96 |
const photo = await cameraRef.current.takePictureAsync({
|
| 97 |
+
quality: 0.7,
|
| 98 |
base64: false,
|
| 99 |
});
|
| 100 |
|
| 101 |
const result = await analyzeImage(photo.uri);
|
| 102 |
|
| 103 |
if (result && result.status === 'success') {
|
| 104 |
+
// In auto mode, we need high confidence to auto-navigate
|
| 105 |
+
// In manual mode, we always navigate on button press
|
| 106 |
+
if (scanMode === 'manual' || (result.confidence > 0.45)) {
|
| 107 |
setIsScanning(false);
|
| 108 |
navigation.navigate('Result', {
|
| 109 |
imageUri: photo.uri,
|
| 110 |
analysisResult: result,
|
| 111 |
language
|
| 112 |
});
|
| 113 |
+
} else {
|
| 114 |
+
setStatusMessage('HOLD STEADY...');
|
| 115 |
}
|
| 116 |
}
|
| 117 |
} catch (error) {
|
| 118 |
console.log("Scanner Error:", error);
|
| 119 |
+
setStatusMessage('POOR LIGHTING? TRY AGAIN');
|
| 120 |
} finally {
|
| 121 |
setLoading(false);
|
| 122 |
+
if (isScanning) setTimeout(() => setStatusMessage(scanMode === 'auto' ? 'LOOKING FOR LEAVES...' : 'READY FOR CAPTURE'), 1000);
|
| 123 |
}
|
| 124 |
};
|
| 125 |
|
| 126 |
+
if (!permission) return <View style={styles.container} />;
|
| 127 |
if (!permission.granted) {
|
| 128 |
return (
|
| 129 |
<View style={styles.container}>
|
| 130 |
+
<View style={styles.permissionBox}>
|
| 131 |
+
<Text style={styles.permissionIcon}>📷</Text>
|
| 132 |
+
<Text style={styles.text}>{language === 'ta' ? "கேமரா அனுமதி தேவை" : "Camera Access Needed"}</Text>
|
| 133 |
+
<TouchableOpacity onPress={requestPermission} style={styles.grantButton}>
|
| 134 |
+
<Text style={styles.grantButtonText}>{t.grantPermission}</Text>
|
| 135 |
+
</TouchableOpacity>
|
| 136 |
+
</View>
|
| 137 |
</View>
|
| 138 |
);
|
| 139 |
}
|
|
|
|
| 145 |
style={StyleSheet.absoluteFill}
|
| 146 |
facing="back"
|
| 147 |
ref={cameraRef}
|
| 148 |
+
shutterSound={false}
|
| 149 |
/>
|
| 150 |
|
| 151 |
+
{/* Immersive Overlay */}
|
| 152 |
+
<View style={styles.maskContainer}>
|
| 153 |
+
<View style={styles.maskSide} />
|
| 154 |
+
<View style={styles.maskMiddle}>
|
| 155 |
+
<View style={styles.maskSide} />
|
| 156 |
+
<View style={styles.scanHole}>
|
| 157 |
+
<Animated.View style={[styles.laser, { transform: [{ translateY: laserAnim }] }]} />
|
| 158 |
+
<Animated.View style={[styles.cornerFrame, { transform: [{ scale: pulseAnim }] }]}>
|
| 159 |
+
<View style={[styles.corner, styles.topLeft]} />
|
| 160 |
+
<View style={[styles.corner, styles.topRight]} />
|
| 161 |
+
<View style={[styles.corner, styles.bottomLeft]} />
|
| 162 |
+
<View style={[styles.corner, styles.bottomRight]} />
|
| 163 |
+
</Animated.View>
|
| 164 |
|
| 165 |
+
{/* Technical HUD Details */}
|
| 166 |
+
<View style={styles.hudTechnicalLeft}>
|
| 167 |
+
<View style={styles.hudLine} />
|
| 168 |
+
<Text style={styles.hudTechText}>LR-24</Text>
|
| 169 |
+
</View>
|
| 170 |
+
<View style={styles.hudTechnicalRight}>
|
| 171 |
+
<Text style={styles.hudTechText}>AF-ON</Text>
|
| 172 |
+
<View style={styles.hudLine} />
|
| 173 |
+
</View>
|
| 174 |
+
</View>
|
| 175 |
+
<View style={styles.maskSide} />
|
| 176 |
</View>
|
| 177 |
+
<View style={styles.maskSide} />
|
| 178 |
+
</View>
|
| 179 |
+
|
| 180 |
+
{/* UI UI Controls */}
|
| 181 |
+
<View style={styles.uiContainer}>
|
| 182 |
+
{/* Header Elevation */}
|
| 183 |
+
<View style={styles.topRegion}>
|
| 184 |
+
<View style={styles.appHeaderRow}>
|
| 185 |
+
<TouchableOpacity onPress={() => navigation.goBack()} style={styles.glassBtn}>
|
| 186 |
+
<Text style={styles.glassBtnText}>✕</Text>
|
| 187 |
+
</TouchableOpacity>
|
| 188 |
|
| 189 |
+
{/* High-Tech Mode Selector */}
|
| 190 |
+
<View style={styles.premiumSwitch}>
|
| 191 |
+
<TouchableOpacity
|
| 192 |
+
style={[styles.switchTab, scanMode === 'auto' && styles.activeSwitchTab]}
|
| 193 |
+
onPress={() => setScanMode('auto')}
|
| 194 |
+
>
|
| 195 |
+
<Text style={[styles.switchTabText, scanMode === 'auto' && styles.activeSwitchTabText]}>
|
| 196 |
+
{t.autoScan}
|
| 197 |
+
</Text>
|
| 198 |
+
</TouchableOpacity>
|
| 199 |
+
<TouchableOpacity
|
| 200 |
+
style={[styles.switchTab, scanMode === 'manual' && styles.activeSwitchTab]}
|
| 201 |
+
onPress={() => setScanMode('manual')}
|
| 202 |
+
>
|
| 203 |
+
<Text style={[styles.switchTabText, scanMode === 'manual' && styles.activeSwitchTabText]}>
|
| 204 |
+
{t.manualCapture}
|
| 205 |
+
</Text>
|
| 206 |
+
</TouchableOpacity>
|
| 207 |
</View>
|
| 208 |
+
|
| 209 |
+
<View style={{ width: 44 }} />
|
| 210 |
+
</View>
|
| 211 |
+
|
| 212 |
+
<View style={styles.statusDisplay}>
|
| 213 |
+
<View style={styles.statusPill}>
|
| 214 |
+
<View style={[styles.pulseDot, { backgroundColor: loading ? COLORS.warning : (scanMode === 'auto' ? '#4ade80' : '#fbbf24') }]} />
|
| 215 |
+
<Text style={styles.statusPillText}>{statusMessage}</Text>
|
|
|
|
|
|
|
| 216 |
</View>
|
| 217 |
+
</View>
|
| 218 |
+
</View>
|
| 219 |
|
| 220 |
+
{/* Footer elevation */}
|
| 221 |
+
<View style={styles.bottomRegion}>
|
| 222 |
+
<View style={styles.hintBox}>
|
| 223 |
+
<Text style={styles.instructionText}>
|
| 224 |
+
{scanMode === 'auto'
|
| 225 |
+
? (language === 'ta' ? "இலையை சட்டத்திற்குள் வைக்கவும்" : "AI IS LOOKING FOR DISEASES AUTOMATICALLY")
|
| 226 |
+
: (language === 'ta' ? "புகைப்படம் எடுக்க பட்டனை அழுத்தவும்" : "TAP TO CAPTURE MANUALLY")}
|
| 227 |
</Text>
|
| 228 |
+
</View>
|
| 229 |
+
|
| 230 |
+
<View style={styles.controlCenter}>
|
| 231 |
+
<TouchableOpacity style={styles.secondaryCircle} onPress={() => navigation.navigate('About')}>
|
| 232 |
+
<Text style={{ fontSize: 24 }}>❓</Text>
|
| 233 |
+
</TouchableOpacity>
|
| 234 |
+
|
| 235 |
+
<View style={styles.mainShutterOuter}>
|
| 236 |
+
<TouchableOpacity
|
| 237 |
+
style={[
|
| 238 |
+
styles.masterShutter,
|
| 239 |
+
loading && styles.shutterBusy,
|
| 240 |
+
scanMode === 'auto' && styles.shutterAutoMode
|
| 241 |
+
]}
|
| 242 |
+
onPress={performCapture}
|
| 243 |
+
disabled={loading}
|
| 244 |
+
>
|
| 245 |
+
<View style={[styles.shutterInnerRing, scanMode === 'auto' && styles.shutterInnerAutoRing]}>
|
| 246 |
+
{loading ? (
|
| 247 |
+
<ActivityIndicator color="white" />
|
| 248 |
+
) : (
|
| 249 |
+
scanMode === 'manual' ? (
|
| 250 |
+
<View style={styles.manualCore} />
|
| 251 |
+
) : (
|
| 252 |
+
<Text style={styles.robotIcon}>🤖</Text>
|
| 253 |
+
)
|
| 254 |
+
)}
|
| 255 |
+
</View>
|
| 256 |
+
</TouchableOpacity>
|
| 257 |
+
</View>
|
| 258 |
+
|
| 259 |
+
<TouchableOpacity
|
| 260 |
+
style={styles.secondaryCircle}
|
| 261 |
+
onPress={() => setIsScanning(!isScanning)}
|
| 262 |
+
>
|
| 263 |
+
<Text style={{ fontSize: 24 }}>{isScanning ? '⏸️' : '▶️'}</Text>
|
| 264 |
+
</TouchableOpacity>
|
| 265 |
+
</View>
|
| 266 |
</View>
|
| 267 |
</View>
|
| 268 |
</View>
|
|
|
|
| 272 |
const styles = StyleSheet.create({
|
| 273 |
container: {
|
| 274 |
flex: 1,
|
| 275 |
+
backgroundColor: '#000',
|
| 276 |
+
},
|
| 277 |
+
maskContainer: {
|
| 278 |
+
...StyleSheet.absoluteFillObject,
|
| 279 |
+
justifyContent: 'center',
|
| 280 |
+
alignItems: 'center',
|
| 281 |
},
|
| 282 |
+
maskSide: {
|
| 283 |
flex: 1,
|
| 284 |
+
width: '100%',
|
| 285 |
+
backgroundColor: 'rgba(0,0,0,0.65)',
|
| 286 |
},
|
| 287 |
+
maskMiddle: {
|
| 288 |
+
flexDirection: 'row',
|
| 289 |
+
height: SCAN_SIZE,
|
| 290 |
+
},
|
| 291 |
+
scanHole: {
|
| 292 |
+
width: SCAN_SIZE,
|
| 293 |
+
height: SCAN_SIZE,
|
| 294 |
+
backgroundColor: 'transparent',
|
| 295 |
+
overflow: 'hidden',
|
| 296 |
+
},
|
| 297 |
+
cornerFrame: {
|
| 298 |
+
...StyleSheet.absoluteFillObject,
|
| 299 |
+
},
|
| 300 |
+
corner: {
|
| 301 |
+
position: 'absolute',
|
| 302 |
+
width: 32,
|
| 303 |
+
height: 32,
|
| 304 |
+
borderColor: '#4ade80',
|
| 305 |
+
},
|
| 306 |
+
topLeft: { top: 0, left: 0, borderTopWidth: 5, borderLeftWidth: 5, borderTopLeftRadius: 18 },
|
| 307 |
+
topRight: { top: 0, right: 0, borderTopWidth: 5, borderRightWidth: 5, borderTopRightRadius: 18 },
|
| 308 |
+
bottomLeft: { bottom: 0, left: 0, borderBottomWidth: 5, borderLeftWidth: 5, borderBottomLeftRadius: 18 },
|
| 309 |
+
bottomRight: { bottom: 0, right: 0, borderBottomWidth: 5, borderRightWidth: 5, borderBottomRightRadius: 18 },
|
| 310 |
+
laser: {
|
| 311 |
+
height: 4,
|
| 312 |
+
width: '100%',
|
| 313 |
+
backgroundColor: '#4ade80',
|
| 314 |
+
shadowColor: '#4ade80',
|
| 315 |
+
shadowOffset: { width: 0, height: 0 },
|
| 316 |
+
shadowOpacity: 1,
|
| 317 |
+
shadowRadius: 15,
|
| 318 |
+
zIndex: 10,
|
| 319 |
+
},
|
| 320 |
+
hudTechnicalLeft: {
|
| 321 |
+
position: 'absolute',
|
| 322 |
+
left: 20,
|
| 323 |
+
top: '40%',
|
| 324 |
flexDirection: 'row',
|
| 325 |
alignItems: 'center',
|
|
|
|
| 326 |
},
|
| 327 |
+
hudTechnicalRight: {
|
| 328 |
+
position: 'absolute',
|
| 329 |
+
right: 20,
|
| 330 |
+
top: '40%',
|
| 331 |
+
flexDirection: 'row',
|
| 332 |
+
alignItems: 'center',
|
| 333 |
},
|
| 334 |
+
hudLine: {
|
| 335 |
+
width: 30,
|
| 336 |
+
height: 1,
|
| 337 |
+
backgroundColor: 'rgba(74, 222, 128, 0.5)',
|
| 338 |
},
|
| 339 |
+
hudTechText: {
|
| 340 |
+
color: 'rgba(74, 222, 128, 0.7)',
|
| 341 |
+
fontSize: 8,
|
| 342 |
+
fontWeight: '900',
|
| 343 |
+
marginHorizontal: 8,
|
| 344 |
+
letterSpacing: 2,
|
| 345 |
},
|
| 346 |
+
uiContainer: {
|
| 347 |
+
...StyleSheet.absoluteFillObject,
|
| 348 |
+
justifyContent: 'space-between',
|
| 349 |
+
paddingVertical: 50,
|
| 350 |
+
},
|
| 351 |
+
topRegion: {
|
| 352 |
+
gap: 20,
|
| 353 |
+
},
|
| 354 |
+
appHeaderRow: {
|
| 355 |
+
flexDirection: 'row',
|
| 356 |
alignItems: 'center',
|
| 357 |
+
justifyContent: 'space-between',
|
| 358 |
+
paddingHorizontal: 25,
|
| 359 |
+
},
|
| 360 |
+
glassBtn: {
|
| 361 |
+
width: 44,
|
| 362 |
+
height: 44,
|
| 363 |
+
borderRadius: 14,
|
| 364 |
+
backgroundColor: 'rgba(255,255,255,0.15)',
|
| 365 |
justifyContent: 'center',
|
| 366 |
+
alignItems: 'center',
|
| 367 |
+
borderWidth: 1,
|
| 368 |
+
borderColor: 'rgba(255,255,255,0.2)',
|
| 369 |
+
},
|
| 370 |
+
glassBtnText: {
|
| 371 |
+
color: COLORS.white,
|
| 372 |
+
fontSize: 18,
|
| 373 |
+
fontWeight: 'bold',
|
| 374 |
},
|
| 375 |
+
premiumSwitch: {
|
| 376 |
+
flexDirection: 'row',
|
| 377 |
+
backgroundColor: 'rgba(0,0,0,0.5)',
|
|
|
|
| 378 |
borderRadius: 20,
|
| 379 |
+
padding: 4,
|
| 380 |
+
borderWidth: 1,
|
| 381 |
+
borderColor: 'rgba(255,255,255,0.15)',
|
| 382 |
},
|
| 383 |
+
switchTab: {
|
| 384 |
+
paddingHorizontal: 22,
|
| 385 |
+
paddingVertical: 8,
|
| 386 |
+
borderRadius: 16,
|
| 387 |
+
},
|
| 388 |
+
activeSwitchTab: {
|
| 389 |
+
backgroundColor: COLORS.white,
|
| 390 |
+
},
|
| 391 |
+
switchTabText: {
|
| 392 |
+
color: COLORS.white,
|
| 393 |
+
fontSize: 11,
|
| 394 |
+
fontWeight: '900',
|
| 395 |
+
letterSpacing: 1,
|
| 396 |
+
},
|
| 397 |
+
activeSwitchTabText: {
|
| 398 |
+
color: COLORS.primaryDark,
|
| 399 |
+
},
|
| 400 |
+
statusDisplay: {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 401 |
alignItems: 'center',
|
| 402 |
},
|
| 403 |
+
statusPill: {
|
| 404 |
+
flexDirection: 'row',
|
|
|
|
|
|
|
|
|
|
| 405 |
alignItems: 'center',
|
| 406 |
+
backgroundColor: 'rgba(0,0,0,0.7)',
|
| 407 |
+
paddingHorizontal: 18,
|
| 408 |
+
paddingVertical: 10,
|
| 409 |
+
borderRadius: 25,
|
| 410 |
+
borderWidth: 1,
|
| 411 |
+
borderColor: 'rgba(74, 222, 128, 0.3)',
|
| 412 |
+
...COLORS.shadow.md,
|
| 413 |
+
},
|
| 414 |
+
pulseDot: {
|
| 415 |
+
width: 10,
|
| 416 |
+
height: 10,
|
| 417 |
+
borderRadius: 5,
|
| 418 |
+
marginRight: 12,
|
| 419 |
+
},
|
| 420 |
+
statusPillText: {
|
| 421 |
+
color: COLORS.white,
|
| 422 |
+
fontSize: 12,
|
| 423 |
+
fontWeight: '900',
|
| 424 |
+
letterSpacing: 1.5,
|
| 425 |
},
|
| 426 |
+
bottomRegion: {
|
| 427 |
+
alignItems: 'center',
|
| 428 |
+
paddingHorizontal: 25,
|
| 429 |
+
},
|
| 430 |
+
hintBox: {
|
| 431 |
+
marginBottom: 40,
|
| 432 |
+
backgroundColor: 'rgba(0,0,0,0.4)',
|
| 433 |
+
paddingHorizontal: 15,
|
| 434 |
+
paddingVertical: 8,
|
| 435 |
+
borderRadius: 12,
|
| 436 |
+
},
|
| 437 |
+
instructionText: {
|
| 438 |
+
color: 'rgba(255,255,255,0.9)',
|
| 439 |
+
fontSize: 11,
|
| 440 |
+
fontWeight: '800',
|
| 441 |
+
letterSpacing: 1,
|
| 442 |
textAlign: 'center',
|
| 443 |
},
|
| 444 |
+
controlCenter: {
|
| 445 |
+
flexDirection: 'row',
|
| 446 |
+
alignItems: 'center',
|
| 447 |
+
justifyContent: 'space-around',
|
|
|
|
| 448 |
width: '100%',
|
| 449 |
},
|
| 450 |
+
secondaryCircle: {
|
| 451 |
+
width: 60,
|
| 452 |
+
height: 60,
|
| 453 |
+
borderRadius: 30,
|
| 454 |
+
backgroundColor: 'rgba(255,255,255,0.15)',
|
| 455 |
+
justifyContent: 'center',
|
| 456 |
+
alignItems: 'center',
|
| 457 |
+
borderWidth: 1,
|
| 458 |
+
borderColor: 'rgba(255,255,255,0.1)',
|
| 459 |
},
|
| 460 |
+
mainShutterOuter: {
|
| 461 |
+
width: 100,
|
| 462 |
+
height: 100,
|
| 463 |
+
borderRadius: 50,
|
| 464 |
+
borderWidth: 3,
|
| 465 |
+
borderColor: 'rgba(255,255,255,0.3)',
|
| 466 |
+
justifyContent: 'center',
|
| 467 |
alignItems: 'center',
|
|
|
|
| 468 |
},
|
| 469 |
+
masterShutter: {
|
| 470 |
+
width: 84,
|
| 471 |
+
height: 84,
|
| 472 |
+
borderRadius: 42,
|
| 473 |
+
backgroundColor: 'rgba(255,255,255,0.1)',
|
| 474 |
+
padding: 5,
|
| 475 |
},
|
| 476 |
+
shutterAutoMode: {
|
| 477 |
+
opacity: 0.8,
|
| 478 |
+
},
|
| 479 |
+
shutterInnerRing: {
|
| 480 |
+
flex: 1,
|
| 481 |
+
borderRadius: 37,
|
| 482 |
backgroundColor: COLORS.white,
|
| 483 |
+
justifyContent: 'center',
|
| 484 |
+
alignItems: 'center',
|
|
|
|
| 485 |
},
|
| 486 |
+
shutterInnerAutoRing: {
|
| 487 |
+
backgroundColor: 'rgba(255,255,255,0.2)',
|
|
|
|
|
|
|
| 488 |
},
|
| 489 |
+
manualCore: {
|
| 490 |
+
width: 60,
|
| 491 |
+
height: 60,
|
| 492 |
+
borderRadius: 30,
|
| 493 |
+
backgroundColor: COLORS.white,
|
| 494 |
+
borderWidth: 4,
|
| 495 |
+
borderColor: 'rgba(0,0,0,0.05)',
|
| 496 |
},
|
| 497 |
+
robotIcon: {
|
| 498 |
+
fontSize: 34,
|
|
|
|
|
|
|
|
|
|
| 499 |
},
|
| 500 |
+
shutterBusy: {
|
| 501 |
+
opacity: 0.5,
|
| 502 |
+
},
|
| 503 |
+
permissionBox: {
|
| 504 |
+
flex: 1,
|
| 505 |
+
justifyContent: 'center',
|
| 506 |
+
alignItems: 'center',
|
| 507 |
+
padding: 30,
|
| 508 |
+
},
|
| 509 |
+
permissionIcon: {
|
| 510 |
+
fontSize: 60,
|
| 511 |
+
marginBottom: 20,
|
| 512 |
+
},
|
| 513 |
+
text: {
|
| 514 |
+
color: COLORS.white,
|
| 515 |
+
fontSize: 18,
|
| 516 |
textAlign: 'center',
|
| 517 |
+
marginBottom: 30,
|
| 518 |
+
fontWeight: '900',
|
| 519 |
},
|
| 520 |
+
grantButton: {
|
| 521 |
+
backgroundColor: COLORS.primary,
|
| 522 |
+
paddingHorizontal: 45,
|
| 523 |
+
paddingVertical: 18,
|
| 524 |
+
borderRadius: 30,
|
| 525 |
+
...COLORS.shadow.md,
|
| 526 |
+
},
|
| 527 |
+
grantButtonText: {
|
| 528 |
+
color: COLORS.white,
|
| 529 |
+
fontWeight: '900',
|
| 530 |
+
fontSize: 16,
|
| 531 |
+
letterSpacing: 1,
|
| 532 |
+
}
|
| 533 |
});
|
| 534 |
|
| 535 |
export default ScannerScreen;
|
screens/SchemesScreen.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
-
import
|
| 2 |
-
import {
|
| 3 |
import { COLORS, SIZES } from '../constants/theme';
|
| 4 |
import { TRANSLATIONS } from '../constants/translations';
|
| 5 |
|
|
@@ -154,6 +154,12 @@ const SchemesScreen = ({ route, navigation }) => {
|
|
| 154 |
const [selectedScheme, setSelectedScheme] = useState(null);
|
| 155 |
const [modalVisible, setModalVisible] = useState(false);
|
| 156 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 157 |
const openSchemeDetail = (scheme) => {
|
| 158 |
setSelectedScheme(scheme);
|
| 159 |
setModalVisible(true);
|
|
@@ -165,93 +171,119 @@ const SchemesScreen = ({ route, navigation }) => {
|
|
| 165 |
onPress={() => openSchemeDetail(scheme)}
|
| 166 |
activeOpacity={0.7}
|
| 167 |
>
|
| 168 |
-
<View style={styles.
|
| 169 |
-
<Text style={styles.
|
| 170 |
</View>
|
| 171 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 172 |
</TouchableOpacity>
|
| 173 |
);
|
| 174 |
|
| 175 |
return (
|
| 176 |
-
<SafeAreaView style={styles.container}>
|
| 177 |
-
<StatusBar backgroundColor={COLORS.
|
| 178 |
-
|
| 179 |
-
{/* Header */}
|
| 180 |
-
<View style={styles.header}>
|
| 181 |
-
<Text style={styles.appName}>GREEN DOCTOR</Text>
|
| 182 |
-
<TouchableOpacity
|
| 183 |
-
onPress={() => navigation.navigate('Language')}
|
| 184 |
-
style={styles.languageButton}
|
| 185 |
-
>
|
| 186 |
-
<Text style={styles.languageIcon}>🌐</Text>
|
| 187 |
-
</TouchableOpacity>
|
| 188 |
-
</View>
|
| 189 |
|
| 190 |
-
{/*
|
| 191 |
-
<View style={styles.
|
| 192 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 193 |
</View>
|
| 194 |
|
| 195 |
-
{/* Schemes List */}
|
| 196 |
<ScrollView
|
| 197 |
style={styles.scrollView}
|
| 198 |
contentContainerStyle={styles.scrollContent}
|
| 199 |
showsVerticalScrollIndicator={false}
|
| 200 |
>
|
| 201 |
-
|
| 202 |
-
<
|
| 203 |
-
|
| 204 |
-
|
|
|
|
|
|
|
|
|
|
| 205 |
</ScrollView>
|
| 206 |
|
| 207 |
-
{/* Scheme Detail Modal */}
|
| 208 |
<Modal
|
| 209 |
animationType="slide"
|
| 210 |
transparent={false}
|
| 211 |
visible={modalVisible}
|
| 212 |
onRequestClose={() => setModalVisible(false)}
|
| 213 |
>
|
| 214 |
-
<SafeAreaView style={styles.
|
| 215 |
-
<View style={styles.
|
| 216 |
<TouchableOpacity
|
| 217 |
onPress={() => setModalVisible(false)}
|
| 218 |
-
style={styles.
|
| 219 |
>
|
| 220 |
-
<Text style={styles.
|
| 221 |
</TouchableOpacity>
|
| 222 |
-
<Text style={styles.
|
| 223 |
-
{selectedScheme?.name[language]}
|
| 224 |
</Text>
|
| 225 |
</View>
|
| 226 |
|
| 227 |
-
<ScrollView style={styles.
|
| 228 |
{selectedScheme && (
|
| 229 |
-
<View style={styles.
|
| 230 |
-
<View style={styles.
|
| 231 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 232 |
</View>
|
| 233 |
|
| 234 |
-
<View style={styles.
|
| 235 |
-
<
|
| 236 |
-
|
|
|
|
|
|
|
| 237 |
</View>
|
| 238 |
|
| 239 |
-
<View style={styles.
|
| 240 |
-
<
|
| 241 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 242 |
</View>
|
| 243 |
|
| 244 |
-
<View style={styles.
|
| 245 |
-
<Text style={styles.
|
| 246 |
-
<Text style={styles.
|
|
|
|
|
|
|
| 247 |
</View>
|
| 248 |
|
| 249 |
-
|
| 250 |
-
<
|
| 251 |
-
|
| 252 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 253 |
|
| 254 |
-
<View style={{ height:
|
| 255 |
</View>
|
| 256 |
)}
|
| 257 |
</ScrollView>
|
|
@@ -264,146 +296,250 @@ const SchemesScreen = ({ route, navigation }) => {
|
|
| 264 |
const styles = StyleSheet.create({
|
| 265 |
container: {
|
| 266 |
flex: 1,
|
| 267 |
-
backgroundColor:
|
| 268 |
},
|
| 269 |
-
|
| 270 |
-
backgroundColor: COLORS.
|
| 271 |
-
|
| 272 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 273 |
flexDirection: 'row',
|
|
|
|
| 274 |
justifyContent: 'space-between',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 275 |
alignItems: 'center',
|
| 276 |
-
borderBottomWidth: 1,
|
| 277 |
-
borderBottomColor: '#e0e0e0',
|
| 278 |
},
|
| 279 |
-
|
| 280 |
-
fontSize:
|
|
|
|
| 281 |
fontWeight: 'bold',
|
| 282 |
-
color: COLORS.primary,
|
| 283 |
-
},
|
| 284 |
-
languageButton: {
|
| 285 |
-
padding: 5,
|
| 286 |
},
|
| 287 |
-
|
| 288 |
-
fontSize:
|
|
|
|
|
|
|
|
|
|
| 289 |
},
|
| 290 |
-
|
| 291 |
-
backgroundColor:
|
| 292 |
-
paddingVertical:
|
| 293 |
-
paddingHorizontal:
|
|
|
|
| 294 |
alignItems: 'center',
|
| 295 |
},
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
letterSpacing:
|
| 301 |
},
|
| 302 |
scrollView: {
|
| 303 |
flex: 1,
|
| 304 |
},
|
| 305 |
scrollContent: {
|
| 306 |
-
|
| 307 |
-
paddingTop: 20,
|
| 308 |
},
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 313 |
marginBottom: 15,
|
|
|
|
|
|
|
|
|
|
| 314 |
flexDirection: 'row',
|
| 315 |
alignItems: 'center',
|
| 316 |
-
|
| 317 |
-
shadowColor: '#000',
|
| 318 |
-
shadowOffset: { width: 0, height: 1 },
|
| 319 |
-
shadowOpacity: 0.1,
|
| 320 |
-
shadowRadius: 2,
|
| 321 |
-
borderWidth: 1.5,
|
| 322 |
-
borderColor: '#4CAF50',
|
| 323 |
-
},
|
| 324 |
-
schemeIconContainer: {
|
| 325 |
-
width: 50,
|
| 326 |
-
height: 50,
|
| 327 |
borderRadius: 25,
|
| 328 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 329 |
justifyContent: 'center',
|
| 330 |
alignItems: 'center',
|
| 331 |
-
marginRight:
|
| 332 |
},
|
| 333 |
-
|
| 334 |
fontSize: 28,
|
| 335 |
},
|
| 336 |
-
|
| 337 |
-
fontSize: 16,
|
| 338 |
-
fontWeight: 'bold',
|
| 339 |
-
color: '#000',
|
| 340 |
flex: 1,
|
| 341 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 342 |
// Modal Styles
|
| 343 |
-
|
| 344 |
flex: 1,
|
| 345 |
backgroundColor: COLORS.background,
|
| 346 |
},
|
| 347 |
-
|
| 348 |
-
backgroundColor: COLORS.
|
| 349 |
paddingVertical: 15,
|
| 350 |
-
paddingHorizontal:
|
| 351 |
flexDirection: 'row',
|
| 352 |
alignItems: 'center',
|
| 353 |
},
|
| 354 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 355 |
marginRight: 15,
|
| 356 |
-
padding: 5,
|
| 357 |
},
|
| 358 |
-
|
| 359 |
-
fontSize:
|
| 360 |
color: COLORS.white,
|
| 361 |
fontWeight: 'bold',
|
| 362 |
},
|
| 363 |
-
|
| 364 |
fontSize: 18,
|
| 365 |
-
fontWeight: '
|
| 366 |
color: COLORS.white,
|
| 367 |
flex: 1,
|
| 368 |
},
|
| 369 |
-
|
| 370 |
flex: 1,
|
| 371 |
},
|
| 372 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 373 |
padding: 20,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 374 |
},
|
| 375 |
-
|
| 376 |
-
width:
|
| 377 |
-
height:
|
| 378 |
-
borderRadius:
|
| 379 |
-
backgroundColor: COLORS.primary + '
|
| 380 |
justifyContent: 'center',
|
| 381 |
alignItems: 'center',
|
| 382 |
-
|
| 383 |
-
marginBottom: 20,
|
| 384 |
},
|
| 385 |
-
|
| 386 |
-
fontSize:
|
| 387 |
},
|
| 388 |
-
|
| 389 |
-
|
| 390 |
-
borderRadius: 12,
|
| 391 |
-
padding: 16,
|
| 392 |
-
marginBottom: 15,
|
| 393 |
-
borderLeftWidth: 4,
|
| 394 |
-
borderLeftColor: COLORS.primary,
|
| 395 |
},
|
| 396 |
-
|
| 397 |
-
fontSize:
|
| 398 |
-
fontWeight: '
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 399 |
color: COLORS.primary,
|
| 400 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 401 |
},
|
| 402 |
-
|
| 403 |
-
fontSize:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 404 |
color: COLORS.text,
|
| 405 |
-
lineHeight:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 406 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 407 |
});
|
| 408 |
|
| 409 |
export default SchemesScreen;
|
|
|
|
| 1 |
+
import { StyleSheet, Text, View, ScrollView, TouchableOpacity, StatusBar, Modal, Linking } from 'react-native';
|
| 2 |
+
import { SafeAreaView } from 'react-native-safe-area-context';
|
| 3 |
import { COLORS, SIZES } from '../constants/theme';
|
| 4 |
import { TRANSLATIONS } from '../constants/translations';
|
| 5 |
|
|
|
|
| 154 |
const [selectedScheme, setSelectedScheme] = useState(null);
|
| 155 |
const [modalVisible, setModalVisible] = useState(false);
|
| 156 |
|
| 157 |
+
const handleApply = (url) => {
|
| 158 |
+
if (url) {
|
| 159 |
+
Linking.openURL(url);
|
| 160 |
+
}
|
| 161 |
+
};
|
| 162 |
+
|
| 163 |
const openSchemeDetail = (scheme) => {
|
| 164 |
setSelectedScheme(scheme);
|
| 165 |
setModalVisible(true);
|
|
|
|
| 171 |
onPress={() => openSchemeDetail(scheme)}
|
| 172 |
activeOpacity={0.7}
|
| 173 |
>
|
| 174 |
+
<View style={styles.schemeIconBox}>
|
| 175 |
+
<Text style={styles.schemeIconEmoji}>{scheme.icon}</Text>
|
| 176 |
</View>
|
| 177 |
+
<View style={styles.schemeInfo}>
|
| 178 |
+
<Text style={styles.schemeTitleText}>{scheme.name[language]}</Text>
|
| 179 |
+
<Text style={styles.schemeSubtitleText} numberOfLines={2}>
|
| 180 |
+
{scheme.shortDesc[language]}
|
| 181 |
+
</Text>
|
| 182 |
+
</View>
|
| 183 |
+
<Text style={styles.schemeArrow}>›</Text>
|
| 184 |
</TouchableOpacity>
|
| 185 |
);
|
| 186 |
|
| 187 |
return (
|
| 188 |
+
<SafeAreaView style={styles.container} edges={['right', 'left', 'top']}>
|
| 189 |
+
<StatusBar backgroundColor={COLORS.primary} barStyle="light-content" />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 190 |
|
| 191 |
+
{/* Immersive Header */}
|
| 192 |
+
<View style={styles.premiumHeader}>
|
| 193 |
+
<View style={styles.headerTopRow}>
|
| 194 |
+
<TouchableOpacity onPress={() => navigation.goBack()} style={styles.backButton}>
|
| 195 |
+
<Text style={styles.backButtonText}>←</Text>
|
| 196 |
+
</TouchableOpacity>
|
| 197 |
+
<Text style={styles.headerTitle}>{t.govtSchemes}</Text>
|
| 198 |
+
<View style={{ width: 44 }} />
|
| 199 |
+
</View>
|
| 200 |
+
<View style={styles.headerSearchPlaceholder}>
|
| 201 |
+
<Text style={styles.searchLabel}>OFFICIAL GOVERNMENT INITIATIVES</Text>
|
| 202 |
+
</View>
|
| 203 |
</View>
|
| 204 |
|
|
|
|
| 205 |
<ScrollView
|
| 206 |
style={styles.scrollView}
|
| 207 |
contentContainerStyle={styles.scrollContent}
|
| 208 |
showsVerticalScrollIndicator={false}
|
| 209 |
>
|
| 210 |
+
<View style={styles.listSection}>
|
| 211 |
+
<Text style={styles.sectionHeading}>AVAILABLE PROGRAMS</Text>
|
| 212 |
+
{SCHEMES_DATA.map((scheme) => (
|
| 213 |
+
<SchemeCard key={scheme.id} scheme={scheme} />
|
| 214 |
+
))}
|
| 215 |
+
</View>
|
| 216 |
+
<View style={{ height: 40 }} />
|
| 217 |
</ScrollView>
|
| 218 |
|
| 219 |
+
{/* Scheme Detail Modal - Redesigned as a Premium Over Sheet */}
|
| 220 |
<Modal
|
| 221 |
animationType="slide"
|
| 222 |
transparent={false}
|
| 223 |
visible={modalVisible}
|
| 224 |
onRequestClose={() => setModalVisible(false)}
|
| 225 |
>
|
| 226 |
+
<SafeAreaView style={styles.modalViewContainer}>
|
| 227 |
+
<View style={styles.modalPremiumHeader}>
|
| 228 |
<TouchableOpacity
|
| 229 |
onPress={() => setModalVisible(false)}
|
| 230 |
+
style={styles.modalBackBtn}
|
| 231 |
>
|
| 232 |
+
<Text style={styles.modalBackBtnText}>✕</Text>
|
| 233 |
</TouchableOpacity>
|
| 234 |
+
<Text style={styles.modalHeaderTitle} numberOfLines={1}>
|
| 235 |
+
{selectedScheme?.name?.[language] || ""}
|
| 236 |
</Text>
|
| 237 |
</View>
|
| 238 |
|
| 239 |
+
<ScrollView style={styles.modalInnerScroll} showsVerticalScrollIndicator={false}>
|
| 240 |
{selectedScheme && (
|
| 241 |
+
<View style={styles.detailWrapper}>
|
| 242 |
+
<View style={styles.heroFeature}>
|
| 243 |
+
<View style={styles.heroEmojiCircle}>
|
| 244 |
+
<Text style={styles.heroEmojiText}>{selectedScheme.icon}</Text>
|
| 245 |
+
</View>
|
| 246 |
+
<View style={styles.heroMeta}>
|
| 247 |
+
<Text style={styles.heroTag}>VERIFIED SCHEME</Text>
|
| 248 |
+
<Text style={styles.heroTitleMain}>{selectedScheme.name[language]}</Text>
|
| 249 |
+
</View>
|
| 250 |
</View>
|
| 251 |
|
| 252 |
+
<View style={styles.contentBlock}>
|
| 253 |
+
<View style={styles.infoPill}>
|
| 254 |
+
<Text style={styles.pillLabel}>📋 PROGRAM OVERVIEW</Text>
|
| 255 |
+
</View>
|
| 256 |
+
<Text style={styles.bodyPara}>{selectedScheme.description[language]}</Text>
|
| 257 |
</View>
|
| 258 |
|
| 259 |
+
<View style={styles.gridBoard}>
|
| 260 |
+
<View style={styles.gridItem}>
|
| 261 |
+
<Text style={styles.gridLabel}>✅ ELIGIBILITY</Text>
|
| 262 |
+
<Text style={styles.gridValue}>{selectedScheme.eligibility[language]}</Text>
|
| 263 |
+
</View>
|
| 264 |
+
<View style={styles.gridItem}>
|
| 265 |
+
<Text style={styles.gridLabel}>🎁 KEY BENEFITS</Text>
|
| 266 |
+
<Text style={styles.gridValue}>{selectedScheme.benefits[language]}</Text>
|
| 267 |
+
</View>
|
| 268 |
</View>
|
| 269 |
|
| 270 |
+
<View style={[styles.contentBlock, { backgroundColor: COLORS.primaryDark, borderRadius: 25, padding: 25 }]}>
|
| 271 |
+
<Text style={[styles.pillLabel, { color: COLORS.secondary }]}>📝 APPLICATION PROCESS</Text>
|
| 272 |
+
<Text style={[styles.bodyPara, { color: 'rgba(255,255,255,0.9)', marginTop: 10 }]}>
|
| 273 |
+
{selectedScheme.howToApply[language]}
|
| 274 |
+
</Text>
|
| 275 |
</View>
|
| 276 |
|
| 277 |
+
{t.schemeLinks && t.schemeLinks[selectedScheme.id] && (
|
| 278 |
+
<TouchableOpacity
|
| 279 |
+
style={styles.ctaButton}
|
| 280 |
+
onPress={() => handleApply(t.schemeLinks[selectedScheme.id])}
|
| 281 |
+
>
|
| 282 |
+
<Text style={styles.ctaButtonText}>{t.applyOnline}</Text>
|
| 283 |
+
</TouchableOpacity>
|
| 284 |
+
)}
|
| 285 |
|
| 286 |
+
<View style={{ height: 60 }} />
|
| 287 |
</View>
|
| 288 |
)}
|
| 289 |
</ScrollView>
|
|
|
|
| 296 |
const styles = StyleSheet.create({
|
| 297 |
container: {
|
| 298 |
flex: 1,
|
| 299 |
+
backgroundColor: COLORS.background,
|
| 300 |
},
|
| 301 |
+
premiumHeader: {
|
| 302 |
+
backgroundColor: COLORS.primary,
|
| 303 |
+
paddingTop: 10,
|
| 304 |
+
paddingBottom: 25,
|
| 305 |
+
paddingHorizontal: SIZES.padding,
|
| 306 |
+
borderBottomLeftRadius: 35,
|
| 307 |
+
borderBottomRightRadius: 35,
|
| 308 |
+
...COLORS.shadow.lg,
|
| 309 |
+
},
|
| 310 |
+
headerTopRow: {
|
| 311 |
flexDirection: 'row',
|
| 312 |
+
alignItems: 'center',
|
| 313 |
justifyContent: 'space-between',
|
| 314 |
+
marginBottom: 20,
|
| 315 |
+
},
|
| 316 |
+
backButton: {
|
| 317 |
+
width: 44,
|
| 318 |
+
height: 44,
|
| 319 |
+
borderRadius: 22,
|
| 320 |
+
backgroundColor: 'rgba(255,255,255,0.2)',
|
| 321 |
+
justifyContent: 'center',
|
| 322 |
alignItems: 'center',
|
|
|
|
|
|
|
| 323 |
},
|
| 324 |
+
backButtonText: {
|
| 325 |
+
fontSize: 24,
|
| 326 |
+
color: COLORS.white,
|
| 327 |
fontWeight: 'bold',
|
|
|
|
|
|
|
|
|
|
|
|
|
| 328 |
},
|
| 329 |
+
headerTitle: {
|
| 330 |
+
fontSize: 20,
|
| 331 |
+
fontWeight: '900',
|
| 332 |
+
color: COLORS.white,
|
| 333 |
+
letterSpacing: 0.5,
|
| 334 |
},
|
| 335 |
+
headerSearchPlaceholder: {
|
| 336 |
+
backgroundColor: 'rgba(0,0,0,0.15)',
|
| 337 |
+
paddingVertical: 8,
|
| 338 |
+
paddingHorizontal: 15,
|
| 339 |
+
borderRadius: 12,
|
| 340 |
alignItems: 'center',
|
| 341 |
},
|
| 342 |
+
searchLabel: {
|
| 343 |
+
color: 'rgba(255,255,255,0.6)',
|
| 344 |
+
fontSize: 10,
|
| 345 |
+
fontWeight: '900',
|
| 346 |
+
letterSpacing: 2,
|
| 347 |
},
|
| 348 |
scrollView: {
|
| 349 |
flex: 1,
|
| 350 |
},
|
| 351 |
scrollContent: {
|
| 352 |
+
padding: SIZES.padding,
|
|
|
|
| 353 |
},
|
| 354 |
+
listSection: {
|
| 355 |
+
paddingBottom: 20,
|
| 356 |
+
},
|
| 357 |
+
sectionHeading: {
|
| 358 |
+
fontSize: 12,
|
| 359 |
+
fontWeight: '900',
|
| 360 |
+
color: COLORS.textLight,
|
| 361 |
+
letterSpacing: 1.5,
|
| 362 |
marginBottom: 15,
|
| 363 |
+
marginLeft: 5,
|
| 364 |
+
},
|
| 365 |
+
schemeCard: {
|
| 366 |
flexDirection: 'row',
|
| 367 |
alignItems: 'center',
|
| 368 |
+
backgroundColor: COLORS.surface,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 369 |
borderRadius: 25,
|
| 370 |
+
padding: 18,
|
| 371 |
+
marginBottom: 15,
|
| 372 |
+
borderWidth: 1,
|
| 373 |
+
borderColor: COLORS.border,
|
| 374 |
+
...COLORS.shadow.sm,
|
| 375 |
+
},
|
| 376 |
+
schemeIconBox: {
|
| 377 |
+
width: 56,
|
| 378 |
+
height: 56,
|
| 379 |
+
borderRadius: 18,
|
| 380 |
+
backgroundColor: COLORS.primary + '10',
|
| 381 |
justifyContent: 'center',
|
| 382 |
alignItems: 'center',
|
| 383 |
+
marginRight: 18,
|
| 384 |
},
|
| 385 |
+
schemeIconEmoji: {
|
| 386 |
fontSize: 28,
|
| 387 |
},
|
| 388 |
+
schemeInfo: {
|
|
|
|
|
|
|
|
|
|
| 389 |
flex: 1,
|
| 390 |
},
|
| 391 |
+
schemeTitleText: {
|
| 392 |
+
fontSize: 17,
|
| 393 |
+
fontWeight: '800',
|
| 394 |
+
color: COLORS.text,
|
| 395 |
+
marginBottom: 4,
|
| 396 |
+
},
|
| 397 |
+
schemeSubtitleText: {
|
| 398 |
+
fontSize: 12,
|
| 399 |
+
color: COLORS.textLight,
|
| 400 |
+
fontWeight: '500',
|
| 401 |
+
lineHeight: 18,
|
| 402 |
+
},
|
| 403 |
+
schemeArrow: {
|
| 404 |
+
fontSize: 24,
|
| 405 |
+
color: COLORS.primary,
|
| 406 |
+
opacity: 0.3,
|
| 407 |
+
fontWeight: '300',
|
| 408 |
+
marginLeft: 10,
|
| 409 |
+
},
|
| 410 |
// Modal Styles
|
| 411 |
+
modalViewContainer: {
|
| 412 |
flex: 1,
|
| 413 |
backgroundColor: COLORS.background,
|
| 414 |
},
|
| 415 |
+
modalPremiumHeader: {
|
| 416 |
+
backgroundColor: COLORS.primaryDark,
|
| 417 |
paddingVertical: 15,
|
| 418 |
+
paddingHorizontal: SIZES.padding,
|
| 419 |
flexDirection: 'row',
|
| 420 |
alignItems: 'center',
|
| 421 |
},
|
| 422 |
+
modalBackBtn: {
|
| 423 |
+
width: 36,
|
| 424 |
+
height: 36,
|
| 425 |
+
borderRadius: 18,
|
| 426 |
+
backgroundColor: 'rgba(255,255,255,0.15)',
|
| 427 |
+
justifyContent: 'center',
|
| 428 |
+
alignItems: 'center',
|
| 429 |
marginRight: 15,
|
|
|
|
| 430 |
},
|
| 431 |
+
modalBackBtnText: {
|
| 432 |
+
fontSize: 18,
|
| 433 |
color: COLORS.white,
|
| 434 |
fontWeight: 'bold',
|
| 435 |
},
|
| 436 |
+
modalHeaderTitle: {
|
| 437 |
fontSize: 18,
|
| 438 |
+
fontWeight: '800',
|
| 439 |
color: COLORS.white,
|
| 440 |
flex: 1,
|
| 441 |
},
|
| 442 |
+
modalInnerScroll: {
|
| 443 |
flex: 1,
|
| 444 |
},
|
| 445 |
+
detailWrapper: {
|
| 446 |
+
padding: SIZES.padding,
|
| 447 |
+
},
|
| 448 |
+
heroFeature: {
|
| 449 |
+
flexDirection: 'row',
|
| 450 |
+
alignItems: 'center',
|
| 451 |
+
backgroundColor: COLORS.surface,
|
| 452 |
+
borderRadius: 30,
|
| 453 |
padding: 20,
|
| 454 |
+
marginBottom: 25,
|
| 455 |
+
...COLORS.shadow.md,
|
| 456 |
+
borderWidth: 1,
|
| 457 |
+
borderColor: COLORS.border,
|
| 458 |
},
|
| 459 |
+
heroEmojiCircle: {
|
| 460 |
+
width: 70,
|
| 461 |
+
height: 70,
|
| 462 |
+
borderRadius: 35,
|
| 463 |
+
backgroundColor: COLORS.primary + '15',
|
| 464 |
justifyContent: 'center',
|
| 465 |
alignItems: 'center',
|
| 466 |
+
marginRight: 20,
|
|
|
|
| 467 |
},
|
| 468 |
+
heroEmojiText: {
|
| 469 |
+
fontSize: 36,
|
| 470 |
},
|
| 471 |
+
heroMeta: {
|
| 472 |
+
flex: 1,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 473 |
},
|
| 474 |
+
heroTag: {
|
| 475 |
+
fontSize: 9,
|
| 476 |
+
fontWeight: '900',
|
| 477 |
+
color: COLORS.success,
|
| 478 |
+
letterSpacing: 2,
|
| 479 |
+
marginBottom: 4,
|
| 480 |
+
},
|
| 481 |
+
heroTitleMain: {
|
| 482 |
+
fontSize: 22,
|
| 483 |
+
fontWeight: '900',
|
| 484 |
+
color: COLORS.text,
|
| 485 |
+
},
|
| 486 |
+
contentBlock: {
|
| 487 |
+
marginBottom: 25,
|
| 488 |
+
},
|
| 489 |
+
pillLabel: {
|
| 490 |
+
fontSize: 11,
|
| 491 |
+
fontWeight: '900',
|
| 492 |
color: COLORS.primary,
|
| 493 |
+
letterSpacing: 1.5,
|
| 494 |
+
marginBottom: 12,
|
| 495 |
+
},
|
| 496 |
+
bodyPara: {
|
| 497 |
+
fontSize: 16,
|
| 498 |
+
color: COLORS.text,
|
| 499 |
+
lineHeight: 26,
|
| 500 |
+
fontWeight: '500',
|
| 501 |
+
},
|
| 502 |
+
gridBoard: {
|
| 503 |
+
flexDirection: 'row',
|
| 504 |
+
gap: 15,
|
| 505 |
+
marginBottom: 25,
|
| 506 |
+
},
|
| 507 |
+
gridItem: {
|
| 508 |
+
flex: 1,
|
| 509 |
+
backgroundColor: COLORS.surface,
|
| 510 |
+
borderRadius: 22,
|
| 511 |
+
padding: 20,
|
| 512 |
+
borderWidth: 1,
|
| 513 |
+
borderColor: COLORS.border,
|
| 514 |
+
...COLORS.shadow.sm,
|
| 515 |
},
|
| 516 |
+
gridLabel: {
|
| 517 |
+
fontSize: 10,
|
| 518 |
+
fontWeight: '900',
|
| 519 |
+
color: COLORS.textLight,
|
| 520 |
+
letterSpacing: 1,
|
| 521 |
+
marginBottom: 8,
|
| 522 |
+
},
|
| 523 |
+
gridValue: {
|
| 524 |
+
fontSize: 14,
|
| 525 |
color: COLORS.text,
|
| 526 |
+
lineHeight: 20,
|
| 527 |
+
fontWeight: '700',
|
| 528 |
+
},
|
| 529 |
+
ctaButton: {
|
| 530 |
+
backgroundColor: COLORS.primary,
|
| 531 |
+
paddingVertical: 20,
|
| 532 |
+
borderRadius: 25,
|
| 533 |
+
alignItems: 'center',
|
| 534 |
+
...COLORS.shadow.lg,
|
| 535 |
+
marginTop: 10,
|
| 536 |
},
|
| 537 |
+
ctaButtonText: {
|
| 538 |
+
color: COLORS.white,
|
| 539 |
+
fontSize: 18,
|
| 540 |
+
fontWeight: '900',
|
| 541 |
+
letterSpacing: 2,
|
| 542 |
+
}
|
| 543 |
});
|
| 544 |
|
| 545 |
export default SchemesScreen;
|
services/aiService.js
CHANGED
|
@@ -25,14 +25,16 @@ export const analyzeImage = async (imageUri) => {
|
|
| 25 |
|
| 26 |
// 1. Try Custom Backend First
|
| 27 |
try {
|
| 28 |
-
console.log("
|
|
|
|
| 29 |
const uploadResult = await FileSystem.uploadAsync(BACKEND_API_URL, imageUri, {
|
| 30 |
httpMethod: 'POST',
|
| 31 |
uploadType: FileSystem.FileSystemUploadType.MULTIPART,
|
| 32 |
fieldName: 'file',
|
| 33 |
});
|
| 34 |
|
| 35 |
-
console.log("
|
|
|
|
| 36 |
|
| 37 |
if (uploadResult.status === 200) {
|
| 38 |
const data = JSON.parse(uploadResult.body);
|
|
@@ -59,9 +61,12 @@ export const analyzeImage = async (imageUri) => {
|
|
| 59 |
isFallback: false,
|
| 60 |
isMock: false
|
| 61 |
}
|
|
|
|
|
|
|
| 62 |
}
|
| 63 |
} catch (e) {
|
| 64 |
-
console.log("
|
|
|
|
| 65 |
}
|
| 66 |
|
| 67 |
// 2. Fallback to Cloud APIs if Render fails
|
|
|
|
| 25 |
|
| 26 |
// 1. Try Custom Backend First
|
| 27 |
try {
|
| 28 |
+
console.log("---------------------------------------------------");
|
| 29 |
+
console.log("DEBUG: Sending request to:", BACKEND_API_URL);
|
| 30 |
const uploadResult = await FileSystem.uploadAsync(BACKEND_API_URL, imageUri, {
|
| 31 |
httpMethod: 'POST',
|
| 32 |
uploadType: FileSystem.FileSystemUploadType.MULTIPART,
|
| 33 |
fieldName: 'file',
|
| 34 |
});
|
| 35 |
|
| 36 |
+
console.log("DEBUG: Response Status:", uploadResult.status);
|
| 37 |
+
console.log("DEBUG: Response Body:", uploadResult.body);
|
| 38 |
|
| 39 |
if (uploadResult.status === 200) {
|
| 40 |
const data = JSON.parse(uploadResult.body);
|
|
|
|
| 61 |
isFallback: false,
|
| 62 |
isMock: false
|
| 63 |
}
|
| 64 |
+
} else {
|
| 65 |
+
console.log("DEBUG: Server returned non-200 status");
|
| 66 |
}
|
| 67 |
} catch (e) {
|
| 68 |
+
console.log("DEBUG: Upload Failed Error:", e);
|
| 69 |
+
console.log("DEBUG: Error Message:", e.message);
|
| 70 |
}
|
| 71 |
|
| 72 |
// 2. Fallback to Cloud APIs if Render fails
|
test_image.txt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:86348476c0970b6b41a68b640229a134bb4d7ab6a229e9980245aeb936b02774
|
| 3 |
+
size 26
|