first commit

This commit is contained in:
2026-03-10 16:18:05 +00:00
commit 11f9c069b5
31635 changed files with 3187747 additions and 0 deletions

14
app/_layout.tsx Normal file
View File

@@ -0,0 +1,14 @@
import { Stack } from 'expo-router';
export default function Layout() {
return (
<Stack screenOptions={{ headerShown: false }}>
{/* O 'index' refere-se ao ficheiro app/index.tsx (o teu Login) */}
<Stack.Screen name="index" options={{ title: 'Login' }} />
<Stack.Screen name="registo" />
<Stack.Screen name="esqueciPalavraPasse" />
<Stack.Screen name="inicio" />
</Stack>
);
}

122
app/esqueciPalavraPasse.tsx Normal file
View File

@@ -0,0 +1,122 @@
import React, { useState } from 'react';
import { View, Text, TextInput, TouchableOpacity, StyleSheet, Alert, ActivityIndicator, SafeAreaView, KeyboardAvoidingView, Platform } from 'react-native';
import { useRouter } from 'expo-router';
import { Colors } from '../Fluxup/src/constants/theme';
export default function ForgotPasswordScreen() {
const router = useRouter();
const [email, setEmail] = useState('');
const [loading, setLoading] = useState(false);
const handleReset = () => {
if (!email) {
Alert.alert('Erro', 'Por favor insira o seu email');
return;
}
setLoading(true);
// simulação de envio de instruções
setTimeout(() => {
setLoading(false);
Alert.alert('Sucesso', 'Um email com instruções foi enviado.');
// o TypeScript não reconhece "/inicio" nas rotas tipadas; forçamos o cast
router.replace('/inicio' as any);
}, 1500);
};
return (
<SafeAreaView style={styles.safe}>
<KeyboardAvoidingView
style={styles.container}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
>
<Text style={styles.title}>Fluxup</Text>
<Text style={styles.subtitle}>Recuperar palavrapasse</Text>
<TextInput
style={styles.input}
placeholder="Email"
placeholderTextColor="#999"
keyboardType="email-address"
autoCapitalize="none"
value={email}
onChangeText={setEmail}
editable={!loading}
/>
<TouchableOpacity
style={[styles.button, loading && styles.buttonDisabled]}
onPress={handleReset}
disabled={loading}
>
{loading ? <ActivityIndicator color="#fff" /> : <Text style={styles.buttonText}>Enviar</Text>}
</TouchableOpacity>
<TouchableOpacity onPress={() => router.back()}>
<Text style={styles.linkText}>Voltar</Text>
</TouchableOpacity>
</KeyboardAvoidingView>
</SafeAreaView>
);
}
const primary = Colors.light.tint;
const styles = StyleSheet.create({
safe: {
flex: 1,
backgroundColor: Colors.light.background,
},
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
backgroundColor: Colors.light.background,
},
title: {
fontSize: 32,
fontWeight: 'bold',
marginBottom: 8,
color: primary,
},
subtitle: {
fontSize: 16,
color: Colors.light.icon,
marginBottom: 24,
},
input: {
width: '100%',
height: 50,
borderRadius: 8,
borderWidth: 1,
borderColor: '#ddd',
paddingHorizontal: 15,
marginBottom: 15,
fontSize: 16,
backgroundColor: '#f9f9f9',
},
button: {
width: '100%',
height: 50,
borderRadius: 8,
backgroundColor: primary,
justifyContent: 'center',
alignItems: 'center',
marginTop: 10,
marginBottom: 20,
},
buttonDisabled: {
backgroundColor: '#ccc',
},
buttonText: {
color: '#fff',
fontSize: 18,
fontWeight: 'bold',
},
linkText: {
color: primary,
fontSize: 14,
marginTop: 10,
},
});

234
app/index.tsx Normal file
View File

@@ -0,0 +1,234 @@
import React, { useState } from 'react';
import {
View,
TextInput,
TouchableOpacity,
Text,
StyleSheet,
Alert,
ActivityIndicator,
SafeAreaView,
KeyboardAvoidingView,
Platform,
} from 'react-native';
import { useRouter } from 'expo-router';
import { usuariosService } from '../Fluxup/src/lib/supabase';
import { Colors } from '../Fluxup/src/constants/theme';
import { Lock, Mail, User } from 'lucide-react-native';
export default function LoginScreen() {
const router = useRouter();
const [email, setEmail] = useState('');
const [palavraPasse, setPalavraPasse] = useState('');
const [idUsuario, setIdUsuario] = useState('');
const [usuario, setUsuario] = useState('');
const [loading, setLoading] = useState(false);
const handleLogin = async () => {
// Validação dos campos
if (!email || !palavraPasse || !idUsuario || !usuario) {
Alert.alert('Erro', 'Por favor, preencha todos os campos');
return;
}
// Validação de email
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
Alert.alert('Erro', 'Email inválido');
return;
}
setLoading(true);
try {
// login no Supabase
const { data, error } = await usuariosService.login(email, palavraPasse);
if (error) {
Alert.alert('Erro', 'Email ou palavra passe incorretos');
return;
}
if (data) {
Alert.alert('Sucesso', `Bem-vindo, ${data.usuario}!`);
// quando o login é bemsucedido, vai para a página inicial
router.replace('/inicio' as any);
}
} catch (err) {
Alert.alert('Erro', 'Erro ao conectar ao servidor');
console.error(err);
} finally {
setLoading(false);
}
};
return (
<SafeAreaView style={styles.safe}>
<KeyboardAvoidingView
style={styles.container}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
>
<Text style={styles.title}>Fluxup</Text>
<Text style={styles.subtitle}>Entre na sua conta</Text>
{/* Campo de Email */}
<View style={styles.inputContainer}>
<Mail size={20} color={Colors.light.icon} style={styles.icon} />
<TextInput
style={styles.input}
placeholder="Email"
placeholderTextColor="#999"
value={email}
onChangeText={setEmail}
keyboardType="email-address"
editable={!loading}
/>
</View>
{/* Campo de Palavra Passe */}
<View style={styles.inputContainer}>
<Lock size={20} color={Colors.light.icon} style={styles.icon} />
<TextInput
style={styles.input}
placeholder="Palavra Passe"
placeholderTextColor="#999"
value={palavraPasse}
onChangeText={setPalavraPasse}
secureTextEntry
editable={!loading}
/>
</View>
{/* Campo de ID Usuário */}
<View style={[styles.inputContainer, { marginBottom: 8 }]}>
<TextInput
style={styles.input}
placeholder="ID Usuário"
placeholderTextColor="#999"
value={idUsuario}
onChangeText={setIdUsuario}
editable={!loading}
/>
</View>
{/* Campo de Usuário */}
<View style={[styles.inputContainer, { marginBottom: 8 }]}>
<TextInput
style={styles.input}
placeholder="Usuário"
placeholderTextColor="#999"
value={usuario}
onChangeText={setUsuario}
editable={!loading}
/>
</View>
{/* Botão de Login */}
<TouchableOpacity
style={[styles.button, loading && styles.buttonDisabled]}
onPress={handleLogin}
disabled={loading}
>
{loading ? (
<ActivityIndicator color="#fff" />
) : (
<Text style={styles.buttonText}>Entrar</Text>
)}
</TouchableOpacity>
<View style={styles.footerLinks}>
{/* Botão de Registo */}
<TouchableOpacity onPress={() => router.push('/registo')}>
<Text style={styles.linkText}>
Não tem conta? <Text style={styles.linkBold}>Registe-se</Text>
</Text>
</TouchableOpacity>
{/* Botão de Esqueci Palavra Passe */}
<TouchableOpacity
onPress={() => router.push('/esqueciPalavraPasse' as any)}
>
<Text style={styles.linkText}>Esqueci a palavrapasse</Text>
</TouchableOpacity>
</View>
</KeyboardAvoidingView>
</SafeAreaView>
);
}
const primary = Colors.light.tint;
const styles = StyleSheet.create({
safe: {
flex: 1,
backgroundColor: Colors.light.background,
},
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
backgroundColor: Colors.light.background,
},
title: {
fontSize: 32,
fontWeight: 'bold',
marginBottom: 8,
color: primary,
},
subtitle: {
fontSize: 16,
color: Colors.light.icon,
marginBottom: 24,
},
inputContainer: {
flexDirection: 'row',
alignItems: 'center',
width: '100%',
backgroundColor: '#fff',
borderRadius: 8,
borderWidth: 1,
borderColor: '#ddd',
marginBottom: 15,
paddingHorizontal: 10,
},
icon: {
marginRight: 10,
},
input: {
flex: 1,
height: 50,
fontSize: 16,
color: Colors.light.text,
},
button: {
width: '100%',
height: 50,
borderRadius: 8,
backgroundColor: primary,
justifyContent: 'center',
alignItems: 'center',
marginTop: 20,
marginBottom: 20,
},
buttonDisabled: {
backgroundColor: '#ccc',
},
buttonText: {
color: '#fff',
fontSize: 18,
fontWeight: 'bold',
},
linkText: {
color: primary,
fontSize: 14,
marginTop: 10,
textAlign: 'center',
},
linkBold: {
fontWeight: 'bold',
},
footerLinks: {
marginTop: 10,
},
});

345
app/inicio.tsx Normal file
View File

@@ -0,0 +1,345 @@
import React, { useState, useEffect, useRef } from 'react';
import {
View,
Text,
TouchableOpacity,
StyleSheet,
SafeAreaView,
ScrollView,
Alert,
} from 'react-native';
import { useRouter } from 'expo-router';
import { Colors, Fonts } from '../src/constants/theme';
import {
Home,
FileText,
BarChart2,
Settings,
Star,
} from 'lucide-react-native';
const PURPLE = '#6a00fa';
const CARD_BG = '#fff';
export default function InicioScreen() {
const router = useRouter();
// --- authentication guard (placeholder) ---
useEffect(() => {
const isAuthenticated = true; // trocar por verificação real
if (!isAuthenticated) {
router.replace('/login' as any);
}
}, []);
// --- checklist state ---
const [tasks, setTasks] = useState([
{ id: 1, label: 'Estudar para intermédio', done: false },
{ id: 2, label: 'Ler artigo técnico', done: false },
{ id: 3, label: 'Fazer exercícios', done: false },
]);
const toggleTask = (id: number) => {
setTasks((prev) =>
prev.map((t) => (t.id === id ? { ...t, done: !t.done } : t))
);
};
// --- habits rating ---
const [rating, setRating] = useState(4);
// --- pomodoro timer ---
const [secondsLeft, setSecondsLeft] = useState(25 * 60);
const [running, setRunning] = useState(false);
const intervalRef = useRef<any>(null);
useEffect(() => {
if (running) {
intervalRef.current = setInterval(() => {
setSecondsLeft((s) => {
if (s <= 1) {
clearInterval(intervalRef.current);
setRunning(false);
return 0;
}
return s - 1;
});
}, 1000);
} else {
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
}
return () => {
if (intervalRef.current) clearInterval(intervalRef.current);
};
}, [running]);
const startTimer = () => {
if (!running) {
setSecondsLeft(25 * 60);
setRunning(true);
}
};
const formatTime = (sec: number) => {
const m = Math.floor(sec / 60)
.toString()
.padStart(2, '0');
const s = (sec % 60).toString().padStart(2, '0');
return `${m}:${s}`;
};
return (
<SafeAreaView style={styles.safe}>
<ScrollView contentContainerStyle={styles.scroll}>
{/* header */}
<View style={styles.header}>
<View>
<Text style={styles.greeting}>Hoje é um bom dia</Text>
<Text style={styles.subtitle}>
Olá, Matheus! Preparado para o seu dia de produtividade?
</Text>
</View>
<Text style={styles.waveEmoji}>👋</Text>
</View>
{/* desafios do dia card */}
<View style={styles.card}>
<Text style={styles.cardTitle}>Desafios do Dia</Text>
{tasks.map((t) => (
<TouchableOpacity
key={t.id}
style={styles.checkItem}
onPress={() => toggleTask(t.id)}
>
<Text style={[styles.checkBox, t.done && styles.done]}></Text>
<Text
style={[
styles.checkLabel,
t.done && styles.checkLabelDone,
]}
>
{t.label}
</Text>
</TouchableOpacity>
))}
<TouchableOpacity
style={styles.addButton}
onPress={() => Alert.alert('Adicionar','implementação futura')}
>
<Text style={styles.addButtonText}>+ Adicionar desafios diários</Text>
</TouchableOpacity>
</View>
{/* hábitos card */}
<View style={styles.card}>
<Text style={styles.cardTitle}>Hábitos</Text>
<View style={styles.ratingRow}>
{Array.from({ length: 5 }).map((_, i) => (
<Star
key={i}
size={20}
color={i < rating ? '#f5c518' : '#ccc'}
onPress={() => setRating(i + 1)}
/>
))}
</View>
<View style={styles.habitsRow}>
<TouchableOpacity style={[styles.habitButton, { backgroundColor: '#A0E9FD' }]}
activeOpacity={0.7}
>
<Text>📘</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.habitButton, { backgroundColor: '#B2F59C' }]}
activeOpacity={0.7}
>
<Text>🧘</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.habitButton, { backgroundColor: '#FFD59C' }]}
activeOpacity={0.7}
>
<Text>🏋</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.habitButton, styles.habitAdd]}
activeOpacity={0.7}
>
<Text>+</Text>
</TouchableOpacity>
</View>
</View>
{/* modo foco card */}
<View style={styles.card}>
<Text style={styles.cardTitle}>Modo Foco</Text>
<View style={styles.focusRow}>
<TouchableOpacity
style={styles.timerBlock}
onPress={startTimer}
>
<Text style={styles.timerText}>{formatTime(secondsLeft)}</Text>
</TouchableOpacity>
<Text style={styles.focusText}>
Produtividade é o segredo do sucesso
</Text>
</View>
</View>
</ScrollView>
{/* bottom navigation */}
<View style={styles.bottomNav}>
<TouchableOpacity style={styles.navItem} disabled>
<Home size={24} color={PURPLE} />
</TouchableOpacity>
<TouchableOpacity style={styles.navItem}>
<FileText size={24} color="#666" />
</TouchableOpacity>
<TouchableOpacity style={styles.navItem}>
<BarChart2 size={24} color="#666" />
</TouchableOpacity>
<TouchableOpacity style={styles.navItem}>
<Settings size={24} color="#666" />
</TouchableOpacity>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
safe: {
flex: 1,
backgroundColor: PURPLE,
},
scroll: {
padding: 20,
paddingBottom: 80,
},
header: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 20,
},
greeting: {
fontSize: 24,
fontWeight: 'bold',
color: '#fff',
},
subtitle: {
fontSize: 14,
color: '#eee',
marginTop: 4,
},
waveEmoji: {
fontSize: 28,
},
card: {
backgroundColor: CARD_BG,
borderRadius: 24,
padding: 16,
marginBottom: 20,
},
cardTitle: {
fontSize: 18,
fontWeight: '600',
marginBottom: 12,
},
checkItem: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 8,
},
checkBox: {
width: 24,
height: 24,
borderWidth: 1,
borderColor: '#666',
marginRight: 8,
textAlign: 'center',
lineHeight: 24,
},
done: {
backgroundColor: '#6a0fa0',
color: '#fff',
borderColor: '#6a0fa0',
},
checkLabel: {
fontSize: 16,
color: '#333',
},
checkLabelDone: {
textDecorationLine: 'line-through',
color: '#999',
},
addButton: {
marginTop: 12,
backgroundColor: PURPLE,
padding: 12,
borderRadius: 8,
alignItems: 'center',
},
addButtonText: {
color: '#fff',
fontWeight: '600',
},
ratingRow: {
flexDirection: 'row',
marginBottom: 12,
},
habitsRow: {
flexDirection: 'row',
justifyContent: 'space-between',
},
habitButton: {
width: 50,
height: 50,
borderRadius: 8,
justifyContent: 'center',
alignItems: 'center',
},
habitAdd: {
borderWidth: 1,
borderStyle: 'dashed',
borderColor: '#666',
backgroundColor: 'transparent',
},
focusRow: {
flexDirection: 'row',
alignItems: 'center',
},
timerBlock: {
width: 80,
height: 80,
backgroundColor: '#3b44f6',
borderRadius: 12,
justifyContent: 'center',
alignItems: 'center',
marginRight: 12,
},
timerText: {
color: '#fff',
fontSize: 20,
fontWeight: 'bold',
},
focusText: {
flex: 1,
fontSize: 14,
color: '#333',
},
bottomNav: {
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
height: 60,
flexDirection: 'row',
justifyContent: 'space-around',
alignItems: 'center',
backgroundColor: CARD_BG,
borderTopWidth: 1,
borderColor: '#ddd',
},
navItem: {
flex: 1,
alignItems: 'center',
},
});

29
app/modal.tsx Normal file
View File

@@ -0,0 +1,29 @@
import { Link } from 'expo-router';
import { StyleSheet } from 'react-native';
import { ThemedText } from '@/components/common/themed-text';
import { ThemedView } from '@/components/common/themed-view';
export default function ModalScreen() {
return (
<ThemedView style={styles.container}>
<ThemedText type="title">This is a modal</ThemedText>
<Link href="/" dismissTo style={styles.link}>
<ThemedText type="link">Go to home screen</ThemedText>
</Link>
</ThemedView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
padding: 20,
},
link: {
marginTop: 15,
paddingVertical: 15,
},
});

300
app/registo.tsx Normal file
View File

@@ -0,0 +1,300 @@
import React, { useState } from 'react';
import {
View,
Text,
TextInput,
TouchableOpacity,
StyleSheet,
Alert,
ActivityIndicator,
KeyboardAvoidingView,
Platform,
ScrollView,
SafeAreaView,
} from 'react-native';
import { useRouter, Link } from 'expo-router';
import { User, Mail, Lock, Phone, ArrowLeft, CheckCircle } from 'lucide-react-native';
import { Colors } from '../Fluxup/src/constants/theme';
// import { supabase } from '../lib/supabase';
export default function RegisterScreen() {
const router = useRouter();
const [username, setUsername] = useState('');
const [email, setEmail] = useState('');
const [phone, setPhone] = useState('');
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [loading, setLoading] = useState(false);
async function handleRegister() {
// 1. Validação simples antes de enviar
if (password !== confirmPassword) {
Alert.alert('Erro', 'As palavras-passe não coincidem.');
return;
}
if (username.length < 3) {
Alert.alert('Erro', 'O nome de utilizador deve ter pelo menos 3 caracteres.');
return;
}
setLoading(true);
// Simulação de registo
setTimeout(() => {
setLoading(false);
Alert.alert('Sucesso', 'Conta criada com sucesso! A entrar...');
// em vez de voltar ao login, encaminhamos directamente para a tela inicial
router.replace('/inicio' as any);
}, 1500);
/* LÓGICA REAL DO SUPABASE (Para depois):
// 1. Criar utilizador na Auth
const { data: authData, error: authError } = await supabase.auth.signUp({
email: email,
password: password,
});
if (authError) {
Alert.alert('Erro', authError.message);
setLoading(false);
return;
}
// 2. Guardar dados extra na tabela 'profiles'
if (authData.user) {
const { error: profileError } = await supabase
.from('profiles')
.insert([{
id: authData.user.id,
username: username,
phone_number: phone,
full_name: username, // Podes mudar isto depois
updated_at: new Date(),
}]);
if (profileError) {
Alert.alert('Aviso', 'Conta criada, mas houve um erro ao salvar o perfil.');
} else {
Alert.alert('Sucesso', 'Verifica o teu email para confirmar a conta!');
router.replace('/inicio');
}
}
setLoading(false);
*/
}
return (
<SafeAreaView style={styles.safe}>
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
style={styles.container}
>
<ScrollView contentContainerStyle={styles.scrollContent}>
{/* Botão Voltar */}
<TouchableOpacity onPress={() => router.back()} style={styles.backButton}>
<ArrowLeft size={24} />
</TouchableOpacity>
<View style={styles.header}>
<Text style={styles.title}>Fluxup</Text>
<Text style={styles.subtitle}>Registar nova conta</Text>
</View>
<View style={styles.form}>
{/* Nome de Utilizador */}
<View style={styles.inputContainer}>
<User size={20} />
<TextInput
style={styles.input}
placeholder="Nome de utilizador"
placeholderTextColor="#999"
value={username}
onChangeText={setUsername}
/>
</View>
{/* Email */}
<View style={styles.inputContainer}>
<Mail size={20} />
<TextInput
style={styles.input}
placeholder="Email"
placeholderTextColor="#999"
value={email}
onChangeText={setEmail}
keyboardType="email-address"
autoCapitalize="none"
/>
</View>
{/* Telemóvel */}
<View style={styles.inputContainer}>
<Phone size={20} />
<TextInput
style={styles.input}
placeholder="Número de telemóvel"
placeholderTextColor="#999"
value={phone}
onChangeText={setPhone}
keyboardType="phone-pad"
/>
</View>
{/* Password */}
<View style={styles.inputContainer}>
<Lock size={20} />
<TextInput
style={styles.input}
placeholder="Palavra-passe"
placeholderTextColor="#999"
value={password}
onChangeText={setPassword}
secureTextEntry
/>
</View>
{/* Confirmar Password */}
<View style={[
styles.inputContainer,
// Muda a cor da borda se as senhas não baterem (e se já tiver escrito algo)
confirmPassword.length > 0 && password !== confirmPassword ? { borderColor: '#EF4444' } : {}
]}>
<Lock size={20} />
<TextInput
style={styles.input}
placeholder="Confirmar palavra-passe"
placeholderTextColor="#999"
value={confirmPassword}
onChangeText={setConfirmPassword}
secureTextEntry
/>
{/* Ícone de verificação verde se as senhas baterem */}
{password.length > 0 && password === confirmPassword && (
<CheckCircle size={20} />
)}
</View>
<TouchableOpacity
style={styles.button}
onPress={handleRegister}
disabled={loading}
>
{loading ? (
<ActivityIndicator color="#fff" />
) : (
<Text style={styles.buttonText}>Registar</Text>
)}
</TouchableOpacity>
<View style={styles.footer}>
<Text style={styles.footerText}> tens conta? </Text>
<Link href="/" asChild>
<TouchableOpacity>
<Text style={styles.linkText}>Entrar</Text>
</TouchableOpacity>
</Link>
</View>
</View>
</ScrollView>
</KeyboardAvoidingView>
</SafeAreaView>
);
}
const primary = Colors.light.tint;
const styles = StyleSheet.create({
safe: {
flex: 1,
backgroundColor: Colors.light.background,
},
container: {
flex: 1,
backgroundColor: Colors.light.background,
},
scrollContent: {
flexGrow: 1,
padding: 24,
justifyContent: 'center',
},
backButton: {
marginTop: 20,
marginBottom: 20,
},
header: {
marginBottom: 30,
alignItems: 'center',
},
title: {
fontSize: 32,
fontWeight: 'bold',
color: primary,
marginBottom: 8,
},
subtitle: {
fontSize: 16,
color: Colors.light.icon,
},
form: {
width: '100%',
},
inputContainer: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#fff',
borderRadius: 12,
paddingHorizontal: 16,
height: 56,
marginBottom: 16,
borderWidth: 1,
borderColor: '#E5E7EB',
},
inputIcon: {
marginRight: 12,
},
input: {
flex: 1,
height: '100%',
color: Colors.light.text,
fontSize: 16,
},
button: {
backgroundColor: primary,
height: 56,
borderRadius: 12,
justifyContent: 'center',
alignItems: 'center',
marginTop: 10,
shadowColor: primary,
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.3,
shadowRadius: 8,
elevation: 4,
},
buttonText: {
color: '#fff',
fontSize: 18,
fontWeight: 'bold',
},
footer: {
flexDirection: 'row',
justifyContent: 'center',
marginTop: 24,
marginBottom: 20,
},
footerText: {
color: Colors.light.icon,
fontSize: 16,
},
linkText: {
color: primary,
fontWeight: 'bold',
fontSize: 16,
},
});