first commit

This commit is contained in:
Rodrigo Nogueira de Sousa
2026-05-22 11:12:19 +01:00
commit 47d2cc65c3
91 changed files with 18528 additions and 0 deletions

318
src/screens/HomeScreen.tsx Normal file
View File

@@ -0,0 +1,318 @@
import React, { useState, useEffect } from 'react';
import {
View,
Text,
ScrollView,
StyleSheet,
Pressable,
FlatList,
Image,
RefreshControl,
} from 'react-native';
import { LinearGradient } from 'expo-linear-gradient';
import { useNavigation } from '@react-navigation/native';
import { StackNavigationProp } from '@react-navigation/stack';
import { RootStackParamList } from '../navigation/types';
import { supabase, isSupabaseConfigured } from '../services/supabase';
import { COLORS, SIZES, FONTS, SHADOWS } from '../constants/theme';
import { Service, Barber, Promotion } from '../types';
import { mockServices, mockBarbers, mockPromotions } from '../data/mockData';
import ServiceCard from '../components/ServiceCard';
import BarberCard from '../components/BarberCard';
const HomeScreen: React.FC = () => {
const navigation = useNavigation<StackNavigationProp<RootStackParamList>>();
const [featuredServices, setFeaturedServices] = useState<Service[]>([]);
const [featuredBarbers, setFeaturedBarbers] = useState<Barber[]>([]);
const [promotions, setPromotions] = useState<Promotion[]>([]);
const [loading, setLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
useEffect(() => {
loadData();
}, []);
const loadData = async () => {
try {
if (!isSupabaseConfigured) {
// Modo demo - usando dados de exemplo
setFeaturedServices(mockServices.slice(0, 4));
setFeaturedBarbers(mockBarbers.filter(b => (b.rating || 0) >= 4.7));
setPromotions(mockPromotions);
setLoading(false);
setRefreshing(false);
return;
}
const [servicesRes, barbersRes, promotionsRes] = await Promise.all([
supabase
.from('services')
.select('*')
.eq('is_active', true)
.limit(5),
supabase
.from('barbers')
.select(`
*,
user:users(name, photo)
`)
.eq('is_active', true)
.gte('rating', 4)
.limit(5),
supabase
.from('promotions')
.select('*')
.eq('is_active', true)
.order('created_at', { ascending: false })
.limit(3),
]);
if (servicesRes.data) setFeaturedServices(servicesRes.data);
if (barbersRes.data) setFeaturedBarbers(barbersRes.data);
if (promotionsRes.data) setPromotions(promotionsRes.data);
} catch (error) {
console.error('Erro ao carregar dados:', error);
// Usando dados de exemplo devido a erro
setFeaturedServices(mockServices.slice(0, 4));
setFeaturedBarbers(mockBarbers.filter(b => (b.rating || 0) >= 4.7));
setPromotions(mockPromotions);
} finally {
setLoading(false);
setRefreshing(false);
}
};
const onRefresh = () => {
setRefreshing(true);
loadData();
};
const renderPromotion = ({ item }: { item: Promotion }) => (
<Pressable
style={({ pressed }) => [
styles.promotionCard,
pressed && styles.promotionCardPressed
]}
onPress={() => navigation.navigate('Services')}
>
<Image source={{ uri: item.image || 'https://via.placeholder.com/300' }} style={styles.promotionImage} />
<LinearGradient
colors={['transparent', 'rgba(0,0,0,0.8)']}
style={styles.promotionOverlay}
>
<Text style={styles.promotionTitle}>{item.title}</Text>
<Text style={styles.promotionDescription}>{item.description}</Text>
<Text style={styles.promotionDiscount}>{item.discount_percentage}% OFF</Text>
</LinearGradient>
</Pressable>
);
const renderService = ({ item }: { item: Service }) => (
<ServiceCard
service={item}
onPress={() => navigation.navigate('ServiceDetail', { serviceId: item.id })}
/>
);
const renderBarber = ({ item }: { item: Barber }) => (
<BarberCard
barber={item}
onPress={() => navigation.navigate('BarberDetail', { barberId: item.id })}
/>
);
return (
<ScrollView
style={styles.container}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} tintColor={COLORS.primary} />
}
>
{/* Hero Section */}
<View style={styles.heroSection}>
<LinearGradient colors={[COLORS.primary, COLORS.accent]} style={styles.heroGradient}>
<Text style={styles.heroTitle}>Barbearia Premium</Text>
<Text style={styles.heroSubtitle}>Experimente o luxo do cuidado masculino</Text>
<Pressable
style={({ pressed }) => [
styles.bookNowButton,
pressed && styles.bookNowButtonPressed
]}
onPress={() => navigation.navigate('Booking')}
>
<Text style={styles.bookNowText}>Marcar Agora</Text>
</Pressable>
</LinearGradient>
</View>
{/* Promotions */}
{promotions.length > 0 && (
<View style={styles.section}>
<Text style={styles.sectionTitle}>Ofertas Especiais</Text>
<FlatList
data={promotions}
renderItem={renderPromotion}
keyExtractor={(item) => item.id}
horizontal
showsHorizontalScrollIndicator={false}
contentContainerStyle={styles.promotionsList}
/>
</View>
)}
{/* Featured Services */}
<View style={styles.section}>
<View style={styles.sectionHeader}>
<Text style={styles.sectionTitle}>Serviços em Destaque</Text>
<Pressable
style={({ pressed }) => [pressed && styles.pressed]}
onPress={() => navigation.navigate('Services')}
>
<Text style={styles.seeAllText}>Ver Todos</Text>
</Pressable>
</View>
<FlatList
data={featuredServices}
renderItem={renderService}
keyExtractor={(item) => item.id}
numColumns={2}
columnWrapperStyle={styles.servicesRow}
scrollEnabled={false}
/>
</View>
{/* Featured Barbers */}
<View style={styles.section}>
<View style={styles.sectionHeader}>
<Text style={styles.sectionTitle}>Barbeiros Mais Avaliados</Text>
<Pressable
style={({ pressed }) => [pressed && styles.pressed]}
onPress={() => navigation.navigate('Barbers')}
>
<Text style={styles.seeAllText}>Ver Todos</Text>
</Pressable>
</View>
<FlatList
data={featuredBarbers}
renderItem={renderBarber}
keyExtractor={(item) => item.id}
scrollEnabled={false}
/>
</View>
</ScrollView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: COLORS.background,
},
heroSection: {
margin: SIZES.margin,
borderRadius: SIZES.radius,
overflow: 'hidden',
...SHADOWS.heavy,
},
heroGradient: {
padding: SIZES.padding * 2,
alignItems: 'center',
},
heroTitle: {
...FONTS.h1,
color: COLORS.background,
textAlign: 'center',
marginBottom: SIZES.base,
},
heroSubtitle: {
...FONTS.body,
color: COLORS.background,
textAlign: 'center',
marginBottom: SIZES.margin * 2,
opacity: 0.9,
},
bookNowButton: {
backgroundColor: COLORS.background,
paddingHorizontal: SIZES.padding * 2,
paddingVertical: SIZES.padding,
borderRadius: SIZES.radius,
cursor: 'pointer',
},
bookNowButtonPressed: {
opacity: 0.8,
},
bookNowText: {
...FONTS.body,
color: COLORS.primary,
fontWeight: 'bold',
},
section: {
margin: SIZES.margin,
},
sectionHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: SIZES.margin,
},
sectionTitle: {
...FONTS.h2,
color: COLORS.text,
},
seeAllText: {
...FONTS.body,
color: COLORS.primary,
},
pressed: {
opacity: 0.7,
},
promotionsList: {
paddingRight: SIZES.padding,
},
promotionCard: {
width: 280,
height: 160,
marginRight: SIZES.margin,
borderRadius: SIZES.radius,
overflow: 'hidden',
...SHADOWS.medium,
cursor: 'pointer',
},
promotionCardPressed: {
opacity: 0.9,
},
promotionImage: {
width: '100%',
height: '100%',
resizeMode: 'cover',
},
promotionOverlay: {
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
padding: SIZES.padding,
justifyContent: 'flex-end',
},
promotionTitle: {
...FONTS.h3,
color: COLORS.text,
marginBottom: SIZES.base / 2,
},
promotionDescription: {
...FONTS.caption,
color: COLORS.text,
marginBottom: SIZES.base,
opacity: 0.9,
},
promotionDiscount: {
...FONTS.h3,
color: COLORS.accent,
fontWeight: 'bold',
},
servicesRow: {
justifyContent: 'space-between',
},
});
export default HomeScreen;