first commit
This commit is contained in:
318
src/screens/HomeScreen.tsx
Normal file
318
src/screens/HomeScreen.tsx
Normal 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;
|
||||
Reference in New Issue
Block a user