diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 4a666ae..30f8b78 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -23,6 +23,7 @@ "expo-font": "~14.0.11", "expo-linear-gradient": "~15.0.8", "expo-status-bar": "~3.0.9", + "lucide-react-native": "^1.12.0", "nativewind": "^4.2.1", "react": "19.1.0", "react-dom": "19.1.0", @@ -11106,6 +11107,17 @@ "yallist": "^3.0.2" } }, + "node_modules/lucide-react-native": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/lucide-react-native/-/lucide-react-native-1.12.0.tgz", + "integrity": "sha512-qw9fMGWTFiGNHVoYOv2N5FzXJaP1/UwVbJAbATNGzJQPGWXVQXITjZUpTjZ/QWLWPz4tBYobn+dhDWGRePLqvg==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-native": "*", + "react-native-svg": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0" + } + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 43a8f61..c9e5431 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -32,6 +32,7 @@ "expo-font": "~14.0.11", "expo-linear-gradient": "~15.0.8", "expo-status-bar": "~3.0.9", + "lucide-react-native": "^1.12.0", "nativewind": "^4.2.1", "react": "19.1.0", "react-dom": "19.1.0", diff --git a/frontend/src/components/AddFoodModal.tsx b/frontend/src/components/AddFoodModal.tsx index 543c8be..bf7f9f0 100644 --- a/frontend/src/components/AddFoodModal.tsx +++ b/frontend/src/components/AddFoodModal.tsx @@ -59,7 +59,7 @@ export function AddFoodModal({ visible, onClose }: Props) { placeholder="e.g. Milk, Wheat, Soy..." chipClassName='bg-remetra-burgundy' chipTextClassName='text-white' - removeTextClassName='text-white' + removeIconColor='#ffffff' onAdd={(ing) => setIngredients((prev) => [...prev, ing])} onRemove={(i) => setIngredients((prev) => prev.filter((_, idx) => idx !== i))} /> diff --git a/frontend/src/components/BarcodeScannerScreen.tsx b/frontend/src/components/BarcodeScannerScreen.tsx index f2e9abf..df4c653 100644 --- a/frontend/src/components/BarcodeScannerScreen.tsx +++ b/frontend/src/components/BarcodeScannerScreen.tsx @@ -1,6 +1,7 @@ import { View, Text, TouchableOpacity, TextInput, StyleSheet } from "react-native"; import { useNavigation, useRoute } from "@react-navigation/native"; import { CameraView, useCameraPermissions } from "expo-camera"; +import { ArrowLeft } from "lucide-react-native"; import { useState, useRef } from "react"; import { useBankStore } from "../store/bankStore"; @@ -91,7 +92,10 @@ export const BarcodeScannerScreen = () => { Point at a barcode navigation.goBack()}> - ← Cancel + + + Cancel + {scanned && ( setScanned(false)}> diff --git a/frontend/src/components/FoodLogForm.tsx b/frontend/src/components/FoodLogForm.tsx index 860386a..7d0359e 100644 --- a/frontend/src/components/FoodLogForm.tsx +++ b/frontend/src/components/FoodLogForm.tsx @@ -7,6 +7,7 @@ import { useNavigation } from '@react-navigation/native'; import { useUIStore } from "../store/uiStore"; import { useState, useEffect } from "react"; import { View, Text, TouchableOpacity, TextInput } from "react-native"; +import { ArrowLeft, Camera } from "lucide-react-native"; import { LogDateTimePicker } from "./LogDateTimePicker"; interface FoodLogFormProps { @@ -96,14 +97,19 @@ export const FoodLogForm: React.FC = ({ onSubmit, onBack, onCl return ( - { - if (!selectedFood && !isCustom) { - onBack() - } else { - setIsCustom(false); setSearchQuery(""); setSelectedFood(null) - } - }}> - ← Back + { + if (!selectedFood && !isCustom) { + onBack() + } else { + setIsCustom(false); setSearchQuery(""); setSelectedFood(null) + } + }} + className="flex-row items-center gap-1.5 mb-4" + hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }} + > + + Back @@ -111,13 +117,15 @@ export const FoodLogForm: React.FC = ({ onSubmit, onBack, onCl {!selectedFood && !isCustom && ( { onCloseModal(); setTimeout(() => navigation.navigate('BarcodeScanner'), 300); }} + hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }} + accessibilityLabel="Scan Barcode" > - Scan Barcode + )} @@ -187,7 +195,7 @@ export const FoodLogForm: React.FC = ({ onSubmit, onBack, onCl placeholder="Add Ingredients..." chipClassName='bg-remetra-burgundy/80' chipTextClassName='text-white' - removeTextClassName='text-white' + removeIconColor='#ffffff' onAdd={(ing) => setCustomIngredients((prev) => [...prev, ing])} onRemove={(i) => setCustomIngredients((prev) => prev.filter((_, idx) => idx !== i))} /> diff --git a/frontend/src/components/GenericChipComponent.tsx b/frontend/src/components/GenericChipComponent.tsx index 2fc6a9d..f45fb51 100644 --- a/frontend/src/components/GenericChipComponent.tsx +++ b/frontend/src/components/GenericChipComponent.tsx @@ -5,6 +5,7 @@ import { TextInput, TouchableOpacity, } from "react-native"; +import { X } from "lucide-react-native"; interface ChipsProps { items: string[]; @@ -14,7 +15,7 @@ interface ChipsProps { onRemove: (index: number) => void; chipClassName?: string; chipTextClassName?: string; - removeTextClassName?: string; + removeIconColor?: string; } export const Chips: React.FC = ({ @@ -25,7 +26,7 @@ export const Chips: React.FC = ({ onRemove, chipClassName = "bg-neutral-200", chipTextClassName = "text-neutral-700", - removeTextClassName = "text-neutral-500" + removeIconColor = "#737373" }) => { const [input, setInput] = useState(""); @@ -45,8 +46,8 @@ export const Chips: React.FC = ({ className={`flex-row items-center rounded-full py-1.5 px-3 gap-1.5 ${chipClassName}`} > {item} - onRemove(i)}> - + onRemove(i)} hitSlop={{ top: 6, bottom: 6, left: 6, right: 6 }}> + ))} diff --git a/frontend/src/components/ItemBank.tsx b/frontend/src/components/ItemBank.tsx index 41d632f..7a57de9 100644 --- a/frontend/src/components/ItemBank.tsx +++ b/frontend/src/components/ItemBank.tsx @@ -1,4 +1,5 @@ import { View, Text, TouchableOpacity } from 'react-native'; +import { X } from 'lucide-react-native'; interface ItemBankProps { title: string; @@ -36,8 +37,9 @@ export function ItemBank({ onRemove(item.id)} hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }} + className="mr-2" > - + {item.name} diff --git a/frontend/src/components/LogEntryModal.tsx b/frontend/src/components/LogEntryModal.tsx index aa5d32c..834eb88 100644 --- a/frontend/src/components/LogEntryModal.tsx +++ b/frontend/src/components/LogEntryModal.tsx @@ -1,5 +1,6 @@ import { Modal, ScrollView, View, TouchableOpacity, Text } from "react-native"; import { useState, useEffect } from "react"; +import { X, Utensils, Stethoscope } from "lucide-react-native"; import { LogEntry, ModalStep } from "../types/logs"; import { FoodLogForm } from "./FoodLogForm"; @@ -49,8 +50,8 @@ const LogEntryModal: React.FC = ({ visible, onClose, onLogEn onStartShouldSetResponder={() => true} > - - + + @@ -65,7 +66,7 @@ const LogEntryModal: React.FC = ({ visible, onClose, onLogEn className="w-full flex-row items-center gap-4 border border-remetra-burgundy rounded-2xl p-5 bg-remetra-orange/10" onPress={() => setStep("food")} > - 🍽 + Food @@ -73,7 +74,7 @@ const LogEntryModal: React.FC = ({ visible, onClose, onLogEn className="w-full flex-row items-center gap-4 border border-remetra-burgundy rounded-2xl p-5 bg-remetra-orange/10" onPress={() => setStep("symptom")} > - 🩺 + Symptom diff --git a/frontend/src/components/SymptomCard.tsx b/frontend/src/components/SymptomCard.tsx index c4aa06e..fb3141b 100644 --- a/frontend/src/components/SymptomCard.tsx +++ b/frontend/src/components/SymptomCard.tsx @@ -1,5 +1,6 @@ import React, { useState } from 'react'; import { Text, TouchableOpacity, View } from 'react-native'; +import { Check } from 'lucide-react-native'; import { symptomLogService } from '../api/symptom_log_service'; import { LogDateTimePicker } from './LogDateTimePicker'; import { SymptomTimelineEntry } from '../types/timeline'; @@ -142,7 +143,7 @@ export function SymptomCard({ hasDuration ? 'bg-remetra-burgundy border-remetra-burgundy' : 'bg-white border-remetra-border' }`} > - {hasDuration ? : null} + {hasDuration ? : null} Duration (minutes) diff --git a/frontend/src/components/SymptomLogForm.tsx b/frontend/src/components/SymptomLogForm.tsx index 78a2f8b..23fc352 100644 --- a/frontend/src/components/SymptomLogForm.tsx +++ b/frontend/src/components/SymptomLogForm.tsx @@ -4,6 +4,7 @@ import { symptomLogService } from "../api/symptom_log_service"; import { useAuthStore } from "../store/useAuthStore"; import { useState } from "react"; import { View, Text, TouchableOpacity, TextInput } from "react-native"; +import { ArrowLeft } from "lucide-react-native"; import { Dropdown } from "react-native-element-dropdown"; import { LogDateTimePicker } from "./LogDateTimePicker"; import { sensationOptions, locationOptions } from "../types/symptomOptions"; @@ -100,17 +101,20 @@ export const SymptomLogForm: React.FC = ({ onSubmit, onBack return ( - { - if (!selectedSymptom && !isCustom) { - onBack() - } else { - setIsCustom(false); setSearchQuery(""); setSelectedSymptom(null); - setCustomSensation(""); setCustomLocation(""); - } - onBack - } - }> - ← Back + { + if (!selectedSymptom && !isCustom) { + onBack() + } else { + setIsCustom(false); setSearchQuery(""); setSelectedSymptom(null); + setCustomSensation(""); setCustomLocation(""); + } + }} + className="flex-row items-center gap-1.5 mb-4" + hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }} + > + + Back diff --git a/frontend/src/navigation/MainTabs.tsx b/frontend/src/navigation/MainTabs.tsx index 69c8748..f5812d9 100644 --- a/frontend/src/navigation/MainTabs.tsx +++ b/frontend/src/navigation/MainTabs.tsx @@ -1,11 +1,12 @@ import React from 'react'; -import { View, Text } from 'react-native'; +import { View } from 'react-native'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import { TimelineScreen } from '../screens/main/TimelineScreen'; import { AnalysisScreen } from '../screens/main/AnalysisScreen'; import { ProfileScreen } from '../screens/main/ProfileScreen'; import LogEntryModal from '../components/LogEntryModal'; import { useUIStore } from '../store/uiStore'; +import { ClipboardList, ChartColumn, User, type LucideIcon } from 'lucide-react-native'; export type MainTabParamList = { History: undefined; @@ -15,10 +16,13 @@ export type MainTabParamList = { const Tab = createBottomTabNavigator(); -function TabIcon({ emoji, focused }: { emoji: string; focused: boolean }) { +const TAB_ACTIVE_COLOR = '#B8624F'; +const TAB_INACTIVE_COLOR = '#b2939b'; + +function TabIcon({ Icon, focused }: { Icon: LucideIcon; focused: boolean }) { return ( - {emoji} + ); } @@ -54,7 +58,7 @@ export function MainTabs() { component={TimelineScreen} options={{ tabBarIcon: ({ focused }) => ( - + ), }} /> @@ -63,7 +67,7 @@ export function MainTabs() { component={AnalysisScreen} options={{ tabBarIcon: ({ focused }) => ( - + ), }} /> @@ -72,7 +76,7 @@ export function MainTabs() { component={ProfileScreen} options={{ tabBarIcon: ({ focused }) => ( - + ), }} /> diff --git a/frontend/src/screens/main/AboutScreen.tsx b/frontend/src/screens/main/AboutScreen.tsx index 9a61107..0528286 100644 --- a/frontend/src/screens/main/AboutScreen.tsx +++ b/frontend/src/screens/main/AboutScreen.tsx @@ -1,5 +1,13 @@ import React from 'react'; import { View, Text, TouchableOpacity, ScrollView, Linking } from 'react-native'; +import { + HeartPlus, + NotebookPen, + Search, + ChartColumn, + ChevronRight, + type LucideIcon, +} from 'lucide-react-native'; import { BackgroundGradient } from '../../components/BackgroundGradient'; const CONTACT_EMAIL = 'hello@remetra.app'; @@ -12,7 +20,7 @@ export function AboutScreen() { {/* Hero */} - ⚕️ + REMETRA @@ -35,9 +43,9 @@ export function AboutScreen() { {/* How it works */} - - - + + + {/* Links */} @@ -87,10 +95,10 @@ function SectionDivider({ label }: { label: string }) { ); } -function InfoRow({ emoji, label }: { emoji: string; label: string }) { +function InfoRow({ Icon, label }: { Icon: LucideIcon; label: string }) { return ( - {emoji} + {label} ); @@ -103,7 +111,7 @@ function LinkRow({ label, onPress }: { label: string; onPress: () => void }) { className="bg-white/70 rounded-xl p-4 flex-row items-center gap-3" > {label} - + ); } diff --git a/frontend/src/screens/main/CorrelationsScreen.tsx b/frontend/src/screens/main/CorrelationsScreen.tsx index ca59993..eb47471 100644 --- a/frontend/src/screens/main/CorrelationsScreen.tsx +++ b/frontend/src/screens/main/CorrelationsScreen.tsx @@ -1,4 +1,5 @@ import { View, Text, ScrollView, TouchableOpacity, ActivityIndicator, TextInput } from 'react-native'; +import { ArrowLeft } from 'lucide-react-native'; import { useEffect, useMemo } from 'react'; import { NativeStackScreenProps } from '@react-navigation/native-stack'; import { BackgroundGradient } from '../../components/BackgroundGradient'; @@ -43,8 +44,13 @@ export function CorrelationsScreen({ navigation, route }: Props) { {/* Header */} - navigation.goBack()} style={{ marginBottom: 8 }}> - ← Back + navigation.goBack()} + style={{ marginBottom: 8, flexDirection: 'row', alignItems: 'center', gap: 6 }} + hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }} + > + + Back diff --git a/frontend/src/screens/main/EditConditionsScreen.tsx b/frontend/src/screens/main/EditConditionsScreen.tsx index 48672a7..3164ad4 100644 --- a/frontend/src/screens/main/EditConditionsScreen.tsx +++ b/frontend/src/screens/main/EditConditionsScreen.tsx @@ -46,7 +46,7 @@ export function EditConditionsScreen() { placeholder="Add condition..." chipClassName='bg-remetra-burgundy opacity-90' chipTextClassName='font-semibold text-remetra-surface-accent' - removeTextClassName='font-semibold text-remetra-surface-accent' + removeIconColor='#fff5f0' onAdd={(item) => setDisease((prev) => [...prev, item])} onRemove={(i) => setDisease((prev) => prev.filter((_, idx) => idx !== i)) diff --git a/frontend/src/screens/main/EditMedicationsScreen.tsx b/frontend/src/screens/main/EditMedicationsScreen.tsx index 6a1a432..c41ef6c 100644 --- a/frontend/src/screens/main/EditMedicationsScreen.tsx +++ b/frontend/src/screens/main/EditMedicationsScreen.tsx @@ -46,7 +46,7 @@ export function EditMedicationsScreen() { placeholder="Add condition..." chipClassName='bg-remetra-burgundy opacity-90' chipTextClassName='font-semibold text-remetra-surface-accent' - removeTextClassName='font-semibold text-remetra-surface-accent' + removeIconColor='#fff5f0' onAdd={(item) => setMedication((prev) => [...prev, item])} onRemove={(i) => setMedication((prev) => prev.filter((_, idx) => idx !== i)) diff --git a/frontend/src/screens/main/ProfileScreen.tsx b/frontend/src/screens/main/ProfileScreen.tsx index efd72c7..46dec2a 100644 --- a/frontend/src/screens/main/ProfileScreen.tsx +++ b/frontend/src/screens/main/ProfileScreen.tsx @@ -2,6 +2,7 @@ import React, { useCallback } from 'react'; import { View, Text, TouchableOpacity, ScrollView } from 'react-native'; import { useNavigation, useFocusEffect } from '@react-navigation/native'; import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; +import { Info, ChevronRight, type LucideIcon } from 'lucide-react-native'; import { BackgroundGradient } from '../../components/BackgroundGradient'; import { useAuthStore } from '../../store/useAuthStore'; import type { MainStackParamList } from '../../navigation/stacks/MainStack'; @@ -68,7 +69,7 @@ export function ProfileScreen() { navigation.navigate('About')} /> @@ -97,11 +98,11 @@ function SectionDivider({ label }: { label: string }) { } function ActionRow({ - emoji, + Icon, label, onPress, }: { - emoji: string; + Icon: LucideIcon; label: string; onPress: () => void; }) { @@ -110,9 +111,9 @@ function ActionRow({ onPress={onPress} className="bg-white/70 rounded-xl p-4 flex-row items-center gap-3" > - {emoji} + {label} - + ); } @@ -147,7 +148,7 @@ function EditableRow({ {value} - + ); @@ -168,7 +169,7 @@ function EditableChipRow({ {label.toUpperCase()} - + {items.length === 0 ? ( diff --git a/frontend/src/screens/main/TimelineScreen.tsx b/frontend/src/screens/main/TimelineScreen.tsx index 6a3279b..c46f223 100644 --- a/frontend/src/screens/main/TimelineScreen.tsx +++ b/frontend/src/screens/main/TimelineScreen.tsx @@ -7,6 +7,7 @@ import { TouchableOpacity, View, } from 'react-native'; +import { ClipboardList } from 'lucide-react-native'; import { BackgroundGradient } from '../../components/BackgroundGradient'; import { FoodCard } from '../../components/FoodCard'; import { SymptomCard } from '../../components/SymptomCard'; @@ -178,7 +179,7 @@ export function TimelineScreen() { Symptom - Tap a card to edit + Tap a card to edit {/* Content */} @@ -198,9 +199,9 @@ export function TimelineScreen() { ) : entries.length === 0 ? ( - 📋 + No logs yet - + Tap + Add Log to record your first food or symptom entry. diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index 259320f..e3b349e 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -18,7 +18,7 @@ module.exports = { 'remetra-terracotta': '#ca5e5e', // chart bar fill 'remetra-mauve': '#b2939b', // screen headers, low intensity, spinners 'remetra-espresso': '#5C2E14', // Dark text on gradient - 'remetra-warm-brown': '#7A4F35', // Warmer brown for secondary text + 'remetra-warm-brown': '#7A4F35', // Warmer brown for secondary // Structural / semantic 'remetra-surface': '#fafafa', // input & list-item backgrounds 'remetra-border': '#ccc', // generic input / card borders