refactor: implement bottom tab navigation and update UI component design system
This commit is contained in:
@@ -36,7 +36,7 @@ export const Button = ({ children, onPress, variant = 'solid', size = 'md', disa
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
{loading ? (
|
||||
<ActivityIndicator size="small" color={variant === 'solid' ? '#fff' : '#6366f1'} />
|
||||
<ActivityIndicator size="small" color={variant === 'solid' ? '#fff' : '#818cf8'} />
|
||||
) : (
|
||||
<Text style={textStyles}>{children}</Text>
|
||||
)}
|
||||
@@ -46,7 +46,7 @@ export const Button = ({ children, onPress, variant = 'solid', size = 'md', disa
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
base: {
|
||||
borderRadius: 8,
|
||||
borderRadius: 14,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexDirection: 'row',
|
||||
@@ -56,38 +56,38 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
outline: {
|
||||
backgroundColor: 'transparent',
|
||||
borderWidth: 2,
|
||||
borderColor: '#6366f1',
|
||||
borderWidth: 1.5,
|
||||
borderColor: 'rgba(99,102,241,0.4)',
|
||||
},
|
||||
ghost: {
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
size_sm: {
|
||||
paddingHorizontal: 12,
|
||||
paddingHorizontal: 14,
|
||||
paddingVertical: 8,
|
||||
},
|
||||
size_md: {
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 10,
|
||||
paddingHorizontal: 18,
|
||||
paddingVertical: 12,
|
||||
},
|
||||
size_lg: {
|
||||
paddingHorizontal: 24,
|
||||
paddingVertical: 14,
|
||||
paddingVertical: 16,
|
||||
},
|
||||
disabled: {
|
||||
opacity: 0.5,
|
||||
opacity: 0.4,
|
||||
},
|
||||
text: {
|
||||
fontWeight: '600',
|
||||
fontWeight: '700',
|
||||
},
|
||||
text_solid: {
|
||||
color: '#fff',
|
||||
},
|
||||
text_outline: {
|
||||
color: '#6366f1',
|
||||
color: '#a5b4fc',
|
||||
},
|
||||
text_ghost: {
|
||||
color: '#6366f1',
|
||||
color: '#a5b4fc',
|
||||
},
|
||||
textSize_sm: {
|
||||
fontSize: 12,
|
||||
@@ -99,5 +99,3 @@ const styles = StyleSheet.create({
|
||||
fontSize: 16,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -12,16 +12,10 @@ export const Card = ({ children, style }: Props) => {
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
card: {
|
||||
backgroundColor: '#fff',
|
||||
borderRadius: 12,
|
||||
backgroundColor: '#141420',
|
||||
borderRadius: 20,
|
||||
padding: 16,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 1 },
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 2,
|
||||
elevation: 2,
|
||||
borderWidth: 1,
|
||||
borderColor: '#e2e8f0',
|
||||
borderColor: 'rgba(255,255,255,0.06)',
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ export const Input = ({ label, error, style, ...props }: Props) => {
|
||||
{label && <Text style={styles.label}>{label}</Text>}
|
||||
<TextInput
|
||||
style={[styles.input, error && styles.inputError, style]}
|
||||
placeholderTextColor="#94a3b8"
|
||||
placeholderTextColor="#475569"
|
||||
{...props}
|
||||
/>
|
||||
{error && <Text style={styles.error}>{error}</Text>}
|
||||
@@ -25,20 +25,22 @@ const styles = StyleSheet.create({
|
||||
marginBottom: 16,
|
||||
},
|
||||
label: {
|
||||
fontSize: 14,
|
||||
fontWeight: '600',
|
||||
color: '#334155',
|
||||
marginBottom: 6,
|
||||
fontSize: 12,
|
||||
fontWeight: '700',
|
||||
color: '#94a3b8',
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: 0.5,
|
||||
marginBottom: 8,
|
||||
},
|
||||
input: {
|
||||
borderWidth: 1,
|
||||
borderColor: '#cbd5e1',
|
||||
borderRadius: 8,
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 10,
|
||||
fontSize: 14,
|
||||
color: '#0f172a',
|
||||
backgroundColor: '#fff',
|
||||
borderColor: 'rgba(255,255,255,0.08)',
|
||||
borderRadius: 14,
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 14,
|
||||
fontSize: 15,
|
||||
color: '#f8fafc',
|
||||
backgroundColor: '#1c1c2e',
|
||||
},
|
||||
inputError: {
|
||||
borderColor: '#ef4444',
|
||||
@@ -46,8 +48,6 @@ const styles = StyleSheet.create({
|
||||
error: {
|
||||
fontSize: 12,
|
||||
color: '#ef4444',
|
||||
marginTop: 4,
|
||||
marginTop: 6,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import React from 'react';
|
||||
import { NavigationContainer } from '@react-navigation/native';
|
||||
import { createNativeStackNavigator } from '@react-navigation/native-stack';
|
||||
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
|
||||
import { View, Text, StyleSheet, Platform } from 'react-native';
|
||||
import { useApp } from '../context/AppContext';
|
||||
import Landing from '../pages/Landing';
|
||||
import AuthLogin from '../pages/AuthLogin';
|
||||
import AuthRegister from '../pages/AuthRegister';
|
||||
import Explore from '../pages/Explore';
|
||||
@@ -12,48 +13,204 @@ import Cart from '../pages/Cart';
|
||||
import Profile from '../pages/Profile';
|
||||
import Dashboard from '../pages/Dashboard';
|
||||
import EventsCreate from '../pages/EventsCreate';
|
||||
import { RootStackParamList } from './types';
|
||||
|
||||
const Stack = createNativeStackNavigator<RootStackParamList>();
|
||||
const Stack = createNativeStackNavigator();
|
||||
const Tab = createBottomTabNavigator();
|
||||
|
||||
const TabIcon = ({ icon, focused }: { icon: string; focused: boolean }) => (
|
||||
<View style={[iconStyles.wrap, focused && iconStyles.wrapActive]}>
|
||||
<Text style={iconStyles.emoji}>{icon}</Text>
|
||||
</View>
|
||||
);
|
||||
|
||||
/* ── Client Tab Stacks ── */
|
||||
function ExploreStack() {
|
||||
return (
|
||||
<Stack.Navigator screenOptions={{ headerShown: false }}>
|
||||
<Stack.Screen name="Explore" component={Explore} />
|
||||
<Stack.Screen name="ShopDetails" component={ShopDetails} />
|
||||
<Stack.Screen name="Booking" component={Booking} />
|
||||
</Stack.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
function CartStack() {
|
||||
return (
|
||||
<Stack.Navigator screenOptions={{ headerShown: false }}>
|
||||
<Stack.Screen name="Cart" component={Cart} />
|
||||
</Stack.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
function ClientProfileStack() {
|
||||
return (
|
||||
<Stack.Navigator screenOptions={{ headerShown: false }}>
|
||||
<Stack.Screen name="Profile" component={Profile} />
|
||||
<Stack.Screen name="EventsCreate" component={EventsCreate} />
|
||||
<Stack.Screen name="ShopDetails" component={ShopDetails} />
|
||||
<Stack.Screen name="Booking" component={Booking} />
|
||||
</Stack.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
function ClientTabs() {
|
||||
const { cart } = useApp();
|
||||
return (
|
||||
<Tab.Navigator
|
||||
screenOptions={{
|
||||
headerShown: false,
|
||||
tabBarStyle: tabStyles.bar,
|
||||
tabBarActiveTintColor: '#a5b4fc',
|
||||
tabBarInactiveTintColor: '#475569',
|
||||
tabBarLabelStyle: tabStyles.label,
|
||||
}}
|
||||
>
|
||||
<Tab.Screen
|
||||
name="ExploreTab"
|
||||
component={ExploreStack}
|
||||
options={{
|
||||
tabBarLabel: 'Explorar',
|
||||
tabBarIcon: ({ focused }) => <TabIcon icon="◉" focused={focused} />,
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="CartTab"
|
||||
component={CartStack}
|
||||
options={{
|
||||
tabBarLabel: 'Carrinho',
|
||||
tabBarIcon: ({ focused }) => <TabIcon icon="◫" focused={focused} />,
|
||||
tabBarBadge: cart.length > 0 ? cart.length : undefined,
|
||||
tabBarBadgeStyle: tabStyles.badge,
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="ProfileTab"
|
||||
component={ClientProfileStack}
|
||||
options={{
|
||||
tabBarLabel: 'Perfil',
|
||||
tabBarIcon: ({ focused }) => <TabIcon icon="◎" focused={focused} />,
|
||||
}}
|
||||
/>
|
||||
</Tab.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
/* ── Barbearia Tab Stacks ── */
|
||||
function DashboardStack() {
|
||||
return (
|
||||
<Stack.Navigator screenOptions={{ headerShown: false }}>
|
||||
<Stack.Screen name="Dashboard" component={Dashboard} />
|
||||
<Stack.Screen name="EventsCreate" component={EventsCreate} />
|
||||
</Stack.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
function BarberProfileStack() {
|
||||
return (
|
||||
<Stack.Navigator screenOptions={{ headerShown: false }}>
|
||||
<Stack.Screen name="Profile" component={Profile} />
|
||||
<Stack.Screen name="EventsCreate" component={EventsCreate} />
|
||||
</Stack.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
function BarbeariaTabs() {
|
||||
return (
|
||||
<Tab.Navigator
|
||||
screenOptions={{
|
||||
headerShown: false,
|
||||
tabBarStyle: tabStyles.bar,
|
||||
tabBarActiveTintColor: '#a5b4fc',
|
||||
tabBarInactiveTintColor: '#475569',
|
||||
tabBarLabelStyle: tabStyles.label,
|
||||
}}
|
||||
>
|
||||
<Tab.Screen
|
||||
name="DashboardTab"
|
||||
component={DashboardStack}
|
||||
options={{
|
||||
tabBarLabel: 'Painel',
|
||||
tabBarIcon: ({ focused }) => <TabIcon icon="▦" focused={focused} />,
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="BarberProfileTab"
|
||||
component={BarberProfileStack}
|
||||
options={{
|
||||
tabBarLabel: 'Perfil',
|
||||
tabBarIcon: ({ focused }) => <TabIcon icon="◎" focused={focused} />,
|
||||
}}
|
||||
/>
|
||||
</Tab.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
/* ── Root Navigator ── */
|
||||
export default function AppNavigator() {
|
||||
const { user } = useApp();
|
||||
|
||||
return (
|
||||
<NavigationContainer>
|
||||
<Stack.Navigator
|
||||
screenOptions={{
|
||||
headerStyle: { backgroundColor: '#6366f1' },
|
||||
headerTintColor: '#fff',
|
||||
headerTitleStyle: { fontWeight: '700' },
|
||||
}}
|
||||
>
|
||||
{!user ? (
|
||||
<>
|
||||
<Stack.Screen name="Landing" component={Landing} options={{ headerShown: false }} />
|
||||
<Stack.Screen name="Login" component={AuthLogin} options={{ title: 'Entrar' }} />
|
||||
<Stack.Screen name="Register" component={AuthRegister} options={{ title: 'Criar Conta' }} />
|
||||
<Stack.Screen name="Explore" component={Explore} options={{ title: 'Explorar' }} />
|
||||
<Stack.Screen name="ShopDetails" component={ShopDetails} options={{ title: 'Detalhes' }} />
|
||||
</>
|
||||
) : user.role === 'barbearia' ? (
|
||||
<>
|
||||
<Stack.Screen name="Dashboard" component={Dashboard} options={{ title: 'Painel', headerShown: false }} />
|
||||
<Stack.Screen name="Profile" component={Profile} options={{ title: 'Perfil' }} />
|
||||
<Stack.Screen name="EventsCreate" component={EventsCreate} options={{ title: 'Criar evento' }} />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Stack.Screen name="Explore" component={Explore} options={{ title: 'Explorar' }} />
|
||||
<Stack.Screen name="ShopDetails" component={ShopDetails} options={{ title: 'Detalhes' }} />
|
||||
<Stack.Screen name="Booking" component={Booking} options={{ title: 'Agendar' }} />
|
||||
<Stack.Screen name="Cart" component={Cart} options={{ title: 'Carrinho' }} />
|
||||
<Stack.Screen name="Profile" component={Profile} options={{ title: 'Perfil' }} />
|
||||
<Stack.Screen name="EventsCreate" component={EventsCreate} options={{ title: 'Criar evento' }} />
|
||||
</>
|
||||
)}
|
||||
</Stack.Navigator>
|
||||
{!user ? (
|
||||
<Stack.Navigator
|
||||
screenOptions={{
|
||||
headerStyle: { backgroundColor: '#0a0a0f' },
|
||||
headerTintColor: '#f8fafc',
|
||||
headerTitleStyle: { fontWeight: '700' },
|
||||
}}
|
||||
>
|
||||
<Stack.Screen name="Login" component={AuthLogin} options={{ headerShown: false }} />
|
||||
<Stack.Screen name="Register" component={AuthRegister} options={{ headerShown: false }} />
|
||||
<Stack.Screen name="Explore" component={Explore} options={{ headerShown: false }} />
|
||||
<Stack.Screen name="ShopDetails" component={ShopDetails} options={{ headerShown: false }} />
|
||||
</Stack.Navigator>
|
||||
) : user.role === 'barbearia' ? (
|
||||
<BarbeariaTabs />
|
||||
) : (
|
||||
<ClientTabs />
|
||||
)}
|
||||
</NavigationContainer>
|
||||
);
|
||||
}
|
||||
|
||||
const tabStyles = StyleSheet.create({
|
||||
bar: {
|
||||
backgroundColor: '#0a0a0f',
|
||||
borderTopColor: 'rgba(255,255,255,0.06)',
|
||||
borderTopWidth: 1,
|
||||
height: Platform.OS === 'ios' ? 88 : 64,
|
||||
paddingBottom: Platform.OS === 'ios' ? 28 : 8,
|
||||
paddingTop: 8,
|
||||
},
|
||||
label: {
|
||||
fontSize: 10,
|
||||
fontWeight: '700',
|
||||
letterSpacing: 0.5,
|
||||
},
|
||||
badge: {
|
||||
backgroundColor: '#6366f1',
|
||||
color: '#fff',
|
||||
fontSize: 10,
|
||||
fontWeight: '800',
|
||||
minWidth: 18,
|
||||
height: 18,
|
||||
lineHeight: 14,
|
||||
},
|
||||
});
|
||||
|
||||
const iconStyles = StyleSheet.create({
|
||||
wrap: {
|
||||
width: 32,
|
||||
height: 32,
|
||||
borderRadius: 10,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
wrapActive: {
|
||||
backgroundColor: 'rgba(99,102,241,0.15)',
|
||||
},
|
||||
emoji: {
|
||||
fontSize: 18,
|
||||
color: '#94a3b8',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
export type RootStackParamList = {
|
||||
Landing: undefined;
|
||||
Login: undefined;
|
||||
Register: undefined;
|
||||
Explore: undefined;
|
||||
|
||||
@@ -6,7 +6,6 @@ import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import { useApp } from '../context/AppContext';
|
||||
import { Button } from '../components/ui/Button';
|
||||
import { Input } from '../components/ui/Input';
|
||||
import { Card } from '../components/ui/Card';
|
||||
import { RootStackParamList } from '../navigation/types';
|
||||
|
||||
export default function AuthLogin() {
|
||||
@@ -23,7 +22,6 @@ export default function AuthLogin() {
|
||||
setError('Preenche email e palavra-passe');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setLoading(true);
|
||||
const nextUser = await login(email.trim(), password);
|
||||
@@ -31,8 +29,6 @@ export default function AuthLogin() {
|
||||
setError('Credenciais inválidas ou email não confirmado');
|
||||
return;
|
||||
}
|
||||
|
||||
navigation.replace(nextUser.role === 'barbearia' ? 'Dashboard' : 'Explore');
|
||||
} catch (e: any) {
|
||||
const message = e?.message || 'Credenciais inválidas ou email não confirmado';
|
||||
setError(message);
|
||||
@@ -45,50 +41,54 @@ export default function AuthLogin() {
|
||||
return (
|
||||
<SafeAreaView style={styles.container}>
|
||||
<ScrollView contentContainerStyle={styles.content} keyboardShouldPersistTaps="handled">
|
||||
<Card style={styles.card}>
|
||||
<TouchableOpacity style={styles.iconBox} onPress={() => navigation.navigate('Landing')}>
|
||||
<Text style={styles.iconText}>SA</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<Text style={styles.title}>Bem-vindo</Text>
|
||||
<Text style={styles.subtitle}>Aceda à sua conta Smart Agenda</Text>
|
||||
|
||||
{!!error && <Text style={styles.error}>{error}</Text>}
|
||||
|
||||
<Input
|
||||
label="Email"
|
||||
value={email}
|
||||
onChangeText={(text) => {
|
||||
setEmail(text);
|
||||
setError('');
|
||||
}}
|
||||
keyboardType="email-address"
|
||||
autoCapitalize="none"
|
||||
placeholder="exemplo@email.com"
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Palavra-passe"
|
||||
value={password}
|
||||
onChangeText={(text) => {
|
||||
setPassword(text);
|
||||
setError('');
|
||||
}}
|
||||
secureTextEntry
|
||||
placeholder="••••••••"
|
||||
/>
|
||||
|
||||
<Button onPress={handleSubmit} style={styles.submitButton} loading={loading}>
|
||||
Entrar na Conta
|
||||
</Button>
|
||||
|
||||
<View style={styles.footer}>
|
||||
<Text style={styles.footerText}>Ainda não tem conta? </Text>
|
||||
<Text style={styles.footerLink} onPress={() => navigation.navigate('Register')}>
|
||||
Criar conta grátis
|
||||
</Text>
|
||||
<View style={styles.logoRow}>
|
||||
<View style={styles.logoBox}>
|
||||
<Text style={styles.logoText}>SA</Text>
|
||||
</View>
|
||||
</Card>
|
||||
</View>
|
||||
|
||||
<Text style={styles.title}>Bem-vindo</Text>
|
||||
<Text style={styles.subtitle}>Aceda à sua conta Smart Agenda</Text>
|
||||
|
||||
{!!error && (
|
||||
<View style={styles.errorBox}>
|
||||
<Text style={styles.errorText}>{error}</Text>
|
||||
</View>
|
||||
)}
|
||||
|
||||
<Input
|
||||
label="Email"
|
||||
value={email}
|
||||
onChangeText={(text) => { setEmail(text); setError(''); }}
|
||||
keyboardType="email-address"
|
||||
autoCapitalize="none"
|
||||
placeholder="exemplo@email.com"
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Palavra-passe"
|
||||
value={password}
|
||||
onChangeText={(text) => { setPassword(text); setError(''); }}
|
||||
secureTextEntry
|
||||
placeholder="••••••••"
|
||||
/>
|
||||
|
||||
<Button onPress={handleSubmit} style={styles.submitButton} loading={loading}>
|
||||
Entrar na Conta
|
||||
</Button>
|
||||
|
||||
<TouchableOpacity style={styles.guestButton} onPress={() => navigation.navigate('Explore')}>
|
||||
<Text style={styles.guestText}>Explorar sem conta</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<View style={styles.divider} />
|
||||
|
||||
<View style={styles.footer}>
|
||||
<Text style={styles.footerText}>Ainda não tem conta? </Text>
|
||||
<Text style={styles.footerLink} onPress={() => navigation.navigate('Register')}>
|
||||
Criar conta grátis
|
||||
</Text>
|
||||
</View>
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
);
|
||||
@@ -97,68 +97,84 @@ export default function AuthLogin() {
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: '#f8fafc',
|
||||
backgroundColor: '#0a0a0f',
|
||||
},
|
||||
content: {
|
||||
padding: 16,
|
||||
padding: 24,
|
||||
justifyContent: 'center',
|
||||
minHeight: '100%',
|
||||
},
|
||||
card: {
|
||||
padding: 24,
|
||||
logoRow: {
|
||||
alignItems: 'center',
|
||||
marginBottom: 32,
|
||||
},
|
||||
iconBox: {
|
||||
width: 64,
|
||||
height: 64,
|
||||
borderRadius: 18,
|
||||
backgroundColor: '#0f172a',
|
||||
logoBox: {
|
||||
width: 72,
|
||||
height: 72,
|
||||
borderRadius: 22,
|
||||
backgroundColor: '#141420',
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(99,102,241,0.3)',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
alignSelf: 'center',
|
||||
marginBottom: 18,
|
||||
},
|
||||
iconText: {
|
||||
logoText: {
|
||||
color: '#818cf8',
|
||||
fontSize: 18,
|
||||
fontSize: 22,
|
||||
fontWeight: '900',
|
||||
},
|
||||
title: {
|
||||
fontSize: 30,
|
||||
fontSize: 34,
|
||||
fontWeight: '900',
|
||||
color: '#0f172a',
|
||||
color: '#f8fafc',
|
||||
textAlign: 'center',
|
||||
marginBottom: 6,
|
||||
},
|
||||
subtitle: {
|
||||
fontSize: 14,
|
||||
fontSize: 15,
|
||||
color: '#64748b',
|
||||
marginBottom: 24,
|
||||
marginBottom: 32,
|
||||
textAlign: 'center',
|
||||
fontWeight: '500',
|
||||
},
|
||||
error: {
|
||||
backgroundColor: '#fff1f2',
|
||||
borderColor: '#fecdd3',
|
||||
errorBox: {
|
||||
backgroundColor: 'rgba(239,68,68,0.1)',
|
||||
borderColor: 'rgba(239,68,68,0.3)',
|
||||
borderWidth: 1,
|
||||
borderRadius: 14,
|
||||
color: '#be123c',
|
||||
padding: 12,
|
||||
padding: 14,
|
||||
marginBottom: 16,
|
||||
},
|
||||
errorText: {
|
||||
color: '#fca5a5',
|
||||
fontWeight: '600',
|
||||
fontSize: 13,
|
||||
},
|
||||
submitButton: {
|
||||
width: '100%',
|
||||
marginTop: 8,
|
||||
backgroundColor: '#0f172a',
|
||||
marginTop: 4,
|
||||
backgroundColor: '#6366f1',
|
||||
paddingVertical: 16,
|
||||
},
|
||||
guestButton: {
|
||||
alignSelf: 'center',
|
||||
marginTop: 16,
|
||||
paddingVertical: 10,
|
||||
},
|
||||
guestText: {
|
||||
color: '#64748b',
|
||||
fontWeight: '700',
|
||||
fontSize: 14,
|
||||
},
|
||||
divider: {
|
||||
height: 1,
|
||||
backgroundColor: 'rgba(255,255,255,0.06)',
|
||||
marginVertical: 24,
|
||||
},
|
||||
footer: {
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'center',
|
||||
marginTop: 24,
|
||||
paddingTop: 24,
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: '#e2e8f0',
|
||||
},
|
||||
footerText: {
|
||||
fontSize: 14,
|
||||
@@ -166,7 +182,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
footerLink: {
|
||||
fontSize: 14,
|
||||
color: '#4f46e5',
|
||||
color: '#818cf8',
|
||||
fontWeight: '800',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -6,7 +6,6 @@ import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import { useApp } from '../context/AppContext';
|
||||
import { Button } from '../components/ui/Button';
|
||||
import { Input } from '../components/ui/Input';
|
||||
import { Card } from '../components/ui/Card';
|
||||
import { RootStackParamList } from '../navigation/types';
|
||||
|
||||
export default function AuthRegister() {
|
||||
@@ -42,8 +41,6 @@ export default function AuthRegister() {
|
||||
navigation.replace('Login');
|
||||
return;
|
||||
}
|
||||
|
||||
navigation.replace(nextUser.role === 'barbearia' ? 'Dashboard' : 'Explore');
|
||||
} catch (e: any) {
|
||||
const message = e?.message || 'Erro ao criar conta';
|
||||
setError(message);
|
||||
@@ -56,67 +53,69 @@ export default function AuthRegister() {
|
||||
return (
|
||||
<SafeAreaView style={styles.container}>
|
||||
<ScrollView contentContainerStyle={styles.content} keyboardShouldPersistTaps="handled">
|
||||
<Card style={styles.card}>
|
||||
<View style={styles.iconBox}>
|
||||
<Text style={styles.iconText}>SA</Text>
|
||||
<View style={styles.logoRow}>
|
||||
<View style={styles.logoBox}>
|
||||
<Text style={styles.logoText}>SA</Text>
|
||||
</View>
|
||||
<Text style={styles.title}>Criar Conta</Text>
|
||||
<Text style={styles.subtitle}>Junte-se à Smart Agenda</Text>
|
||||
</View>
|
||||
|
||||
{!!error && <Text style={styles.error}>{error}</Text>}
|
||||
<Text style={styles.title}>Criar Conta</Text>
|
||||
<Text style={styles.subtitle}>Junte-se à Smart Agenda</Text>
|
||||
|
||||
<Text style={styles.label}>Eu sou...</Text>
|
||||
<View style={styles.roleContainer}>
|
||||
{(['cliente', 'barbearia'] as const).map((item) => (
|
||||
<TouchableOpacity
|
||||
key={item}
|
||||
style={[styles.roleButton, role === item && styles.roleButtonActive]}
|
||||
onPress={() => {
|
||||
setRole(item);
|
||||
setError('');
|
||||
}}
|
||||
>
|
||||
<Text style={[styles.roleText, role === item && styles.roleTextActive]}>
|
||||
{item === 'cliente' ? 'Cliente' : 'Barbearia'}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
{!!error && (
|
||||
<View style={styles.errorBox}>
|
||||
<Text style={styles.errorText}>{error}</Text>
|
||||
</View>
|
||||
)}
|
||||
|
||||
<Input label="Nome completo" value={name} onChangeText={setName} placeholder="Ex: João Silva" />
|
||||
<Text style={styles.label}>Eu sou...</Text>
|
||||
<View style={styles.roleContainer}>
|
||||
{(['cliente', 'barbearia'] as const).map((item) => (
|
||||
<TouchableOpacity
|
||||
key={item}
|
||||
style={[styles.roleButton, role === item && styles.roleButtonActive]}
|
||||
onPress={() => { setRole(item); setError(''); }}
|
||||
>
|
||||
<Text style={[styles.roleEmoji]}>{item === 'cliente' ? '✂️' : '💈'}</Text>
|
||||
<Text style={[styles.roleText, role === item && styles.roleTextActive]}>
|
||||
{item === 'cliente' ? 'Cliente' : 'Barbearia'}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</View>
|
||||
|
||||
<Input label="Nome completo" value={name} onChangeText={setName} placeholder="Ex: João Silva" />
|
||||
<Input
|
||||
label="Email"
|
||||
value={email}
|
||||
onChangeText={(text) => { setEmail(text); setError(''); }}
|
||||
keyboardType="email-address"
|
||||
autoCapitalize="none"
|
||||
placeholder="exemplo@email.com"
|
||||
/>
|
||||
<Input label="Palavra-passe" value={password} onChangeText={setPassword} secureTextEntry placeholder="••••••••" />
|
||||
|
||||
{role === 'barbearia' && (
|
||||
<Input
|
||||
label="Email"
|
||||
value={email}
|
||||
onChangeText={(text) => {
|
||||
setEmail(text);
|
||||
setError('');
|
||||
}}
|
||||
keyboardType="email-address"
|
||||
autoCapitalize="none"
|
||||
placeholder="exemplo@email.com"
|
||||
label="Nome da barbearia"
|
||||
value={shopName}
|
||||
onChangeText={setShopName}
|
||||
placeholder="Ex: Barbearia Estilo"
|
||||
/>
|
||||
<Input label="Palavra-passe" value={password} onChangeText={setPassword} secureTextEntry placeholder="••••••••" />
|
||||
)}
|
||||
|
||||
{role === 'barbearia' && (
|
||||
<Input
|
||||
label="Nome da barbearia"
|
||||
value={shopName}
|
||||
onChangeText={setShopName}
|
||||
placeholder="Ex: Barbearia Estilo"
|
||||
/>
|
||||
)}
|
||||
<Button onPress={handleSubmit} style={styles.submitButton} loading={loading}>
|
||||
Criar minha conta
|
||||
</Button>
|
||||
|
||||
<Button onPress={handleSubmit} style={styles.submitButton} loading={loading}>
|
||||
Criar minha conta
|
||||
</Button>
|
||||
<View style={styles.divider} />
|
||||
|
||||
<View style={styles.footer}>
|
||||
<Text style={styles.footerText}>Já tem uma conta? </Text>
|
||||
<Text style={styles.footerLink} onPress={() => navigation.navigate('Login')}>
|
||||
Fazer Login
|
||||
</Text>
|
||||
</View>
|
||||
</Card>
|
||||
<View style={styles.footer}>
|
||||
<Text style={styles.footerText}>Já tem uma conta? </Text>
|
||||
<Text style={styles.footerLink} onPress={() => navigation.navigate('Login')}>
|
||||
Fazer Login
|
||||
</Text>
|
||||
</View>
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
);
|
||||
@@ -125,102 +124,113 @@ export default function AuthRegister() {
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: '#f8fafc',
|
||||
backgroundColor: '#0a0a0f',
|
||||
},
|
||||
content: {
|
||||
padding: 16,
|
||||
padding: 24,
|
||||
justifyContent: 'center',
|
||||
minHeight: '100%',
|
||||
},
|
||||
card: {
|
||||
padding: 24,
|
||||
logoRow: {
|
||||
alignItems: 'center',
|
||||
marginBottom: 28,
|
||||
},
|
||||
iconBox: {
|
||||
width: 64,
|
||||
height: 64,
|
||||
borderRadius: 18,
|
||||
backgroundColor: '#0f172a',
|
||||
logoBox: {
|
||||
width: 72,
|
||||
height: 72,
|
||||
borderRadius: 22,
|
||||
backgroundColor: '#141420',
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(99,102,241,0.3)',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
alignSelf: 'center',
|
||||
marginBottom: 18,
|
||||
},
|
||||
iconText: {
|
||||
logoText: {
|
||||
color: '#818cf8',
|
||||
fontSize: 18,
|
||||
fontSize: 22,
|
||||
fontWeight: '900',
|
||||
},
|
||||
title: {
|
||||
fontSize: 30,
|
||||
fontSize: 34,
|
||||
fontWeight: '900',
|
||||
color: '#0f172a',
|
||||
color: '#f8fafc',
|
||||
textAlign: 'center',
|
||||
marginBottom: 6,
|
||||
},
|
||||
subtitle: {
|
||||
fontSize: 14,
|
||||
fontSize: 15,
|
||||
color: '#64748b',
|
||||
marginBottom: 24,
|
||||
marginBottom: 28,
|
||||
textAlign: 'center',
|
||||
fontWeight: '500',
|
||||
},
|
||||
error: {
|
||||
backgroundColor: '#fff1f2',
|
||||
borderColor: '#fecdd3',
|
||||
errorBox: {
|
||||
backgroundColor: 'rgba(239,68,68,0.1)',
|
||||
borderColor: 'rgba(239,68,68,0.3)',
|
||||
borderWidth: 1,
|
||||
borderRadius: 14,
|
||||
color: '#be123c',
|
||||
padding: 12,
|
||||
padding: 14,
|
||||
marginBottom: 16,
|
||||
},
|
||||
errorText: {
|
||||
color: '#fca5a5',
|
||||
fontWeight: '600',
|
||||
fontSize: 13,
|
||||
},
|
||||
label: {
|
||||
fontSize: 12,
|
||||
color: '#0f172a',
|
||||
fontWeight: '900',
|
||||
color: '#94a3b8',
|
||||
fontWeight: '700',
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: 0.5,
|
||||
marginBottom: 10,
|
||||
},
|
||||
roleContainer: {
|
||||
flexDirection: 'row',
|
||||
gap: 12,
|
||||
marginBottom: 18,
|
||||
marginBottom: 20,
|
||||
},
|
||||
roleButton: {
|
||||
flex: 1,
|
||||
padding: 16,
|
||||
padding: 18,
|
||||
borderRadius: 16,
|
||||
borderWidth: 2,
|
||||
borderColor: '#f1f5f9',
|
||||
backgroundColor: '#f8fafc',
|
||||
borderWidth: 1.5,
|
||||
borderColor: 'rgba(255,255,255,0.06)',
|
||||
backgroundColor: '#141420',
|
||||
alignItems: 'center',
|
||||
gap: 8,
|
||||
},
|
||||
roleButtonActive: {
|
||||
borderColor: '#0f172a',
|
||||
backgroundColor: '#0f172a',
|
||||
borderColor: '#6366f1',
|
||||
backgroundColor: 'rgba(99,102,241,0.1)',
|
||||
},
|
||||
roleEmoji: {
|
||||
fontSize: 24,
|
||||
},
|
||||
roleText: {
|
||||
fontSize: 13,
|
||||
fontWeight: '900',
|
||||
fontWeight: '800',
|
||||
color: '#64748b',
|
||||
textTransform: 'uppercase',
|
||||
},
|
||||
roleTextActive: {
|
||||
color: '#818cf8',
|
||||
color: '#a5b4fc',
|
||||
},
|
||||
submitButton: {
|
||||
width: '100%',
|
||||
marginTop: 8,
|
||||
backgroundColor: '#0f172a',
|
||||
marginTop: 4,
|
||||
backgroundColor: '#6366f1',
|
||||
paddingVertical: 16,
|
||||
},
|
||||
divider: {
|
||||
height: 1,
|
||||
backgroundColor: 'rgba(255,255,255,0.06)',
|
||||
marginVertical: 24,
|
||||
},
|
||||
footer: {
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'center',
|
||||
marginTop: 24,
|
||||
paddingTop: 24,
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: '#e2e8f0',
|
||||
},
|
||||
footerText: {
|
||||
fontSize: 14,
|
||||
@@ -228,7 +238,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
footerLink: {
|
||||
fontSize: 14,
|
||||
color: '#4f46e5',
|
||||
color: '#818cf8',
|
||||
fontWeight: '800',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -10,7 +10,7 @@ import { RootStackParamList } from '../navigation/types';
|
||||
|
||||
export default function Explore() {
|
||||
const navigation = useNavigation<NativeStackNavigationProp<RootStackParamList>>();
|
||||
const { shops, shopsReady, cart, user, logout } = useApp();
|
||||
const { shops, shopsReady, user } = useApp();
|
||||
const [query, setQuery] = useState('');
|
||||
const [filter, setFilter] = useState<'todas' | 'top' | 'servicos'>('todas');
|
||||
|
||||
@@ -28,111 +28,102 @@ export default function Explore() {
|
||||
});
|
||||
}, [shops, query, filter]);
|
||||
|
||||
const goToProfile = () => {
|
||||
if (!user) navigation.navigate('Login');
|
||||
else navigation.navigate(user.role === 'barbearia' ? 'Dashboard' : 'Profile');
|
||||
};
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.container}>
|
||||
<ScrollView contentContainerStyle={styles.content} keyboardShouldPersistTaps="handled">
|
||||
<View style={styles.topBar}>
|
||||
<TouchableOpacity onPress={() => navigation.navigate(user ? 'Explore' : 'Landing')} style={styles.brand}>
|
||||
<View style={styles.brandIcon}>
|
||||
<Text style={styles.brandIconText}>SA</Text>
|
||||
</View>
|
||||
<Text style={styles.brandText}>Smart Agenda</Text>
|
||||
</TouchableOpacity>
|
||||
<View style={styles.topActions}>
|
||||
{user?.role !== 'barbearia' && (
|
||||
<TouchableOpacity style={styles.cartButton} onPress={() => (user ? navigation.navigate('Cart') : navigation.navigate('Login'))}>
|
||||
<Text style={styles.cartText}>Carrinho</Text>
|
||||
{cart.length > 0 && <Text style={styles.cartBadge}>{cart.length}</Text>}
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
<TouchableOpacity style={styles.profileButton} onPress={goToProfile}>
|
||||
<Text style={styles.profileText}>{user ? user.name.charAt(0).toUpperCase() : 'Entrar'}</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Header */}
|
||||
<View style={styles.header}>
|
||||
<View style={styles.kicker}>
|
||||
<Text style={styles.kickerText}>As nossas barbearias</Text>
|
||||
<View>
|
||||
<Text style={styles.greeting}>
|
||||
{user ? `Olá, ${user.name.split(' ')[0]}` : 'Descobre'}
|
||||
</Text>
|
||||
<Text style={styles.headline}>Barbearias</Text>
|
||||
</View>
|
||||
<View style={styles.brandPill}>
|
||||
<Text style={styles.brandText}>SA</Text>
|
||||
</View>
|
||||
<Text style={styles.title}>Ver <Text style={styles.titleAccent}>Barbearias</Text></Text>
|
||||
<Text style={styles.subtitle}>Descubra barbearias exclusivas e reserve o seu próximo corte em segundos.</Text>
|
||||
{user && (
|
||||
<TouchableOpacity onPress={logout} style={styles.logoutButton}>
|
||||
<Text style={styles.logoutText}>Sair da conta</Text>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</View>
|
||||
|
||||
<Card style={styles.searchCard}>
|
||||
{/* Search */}
|
||||
<View style={styles.searchBox}>
|
||||
<Text style={styles.searchIcon}>⌕</Text>
|
||||
<TextInput
|
||||
value={query}
|
||||
onChangeText={setQuery}
|
||||
placeholder="Pesquisar por nome ou endereço..."
|
||||
placeholderTextColor="#94a3b8"
|
||||
placeholder="Pesquisar nome ou endereço..."
|
||||
placeholderTextColor="#475569"
|
||||
style={styles.searchInput}
|
||||
/>
|
||||
<View style={styles.filters}>
|
||||
{[
|
||||
['todas', 'Melhor avaliação'],
|
||||
['top', 'Top avaliadas'],
|
||||
['servicos', 'Mais serviços'],
|
||||
].map(([id, label]) => (
|
||||
<TouchableOpacity
|
||||
key={id}
|
||||
onPress={() => setFilter(id as typeof filter)}
|
||||
style={[styles.filterChip, filter === id && styles.filterChipActive]}
|
||||
>
|
||||
<Text style={[styles.filterText, filter === id && styles.filterTextActive]}>{label}</Text>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</View>
|
||||
</Card>
|
||||
</View>
|
||||
|
||||
{/* Filters */}
|
||||
<ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={styles.filters}>
|
||||
{[
|
||||
['todas', 'Melhor avaliação'],
|
||||
['top', 'Top avaliadas'],
|
||||
['servicos', 'Mais serviços'],
|
||||
].map(([id, label]) => (
|
||||
<TouchableOpacity
|
||||
key={id}
|
||||
onPress={() => setFilter(id as typeof filter)}
|
||||
style={[styles.chip, filter === id && styles.chipActive]}
|
||||
>
|
||||
<Text style={[styles.chipText, filter === id && styles.chipTextActive]}>{label}</Text>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</ScrollView>
|
||||
|
||||
{/* Results */}
|
||||
{!shopsReady ? (
|
||||
<Card style={styles.emptyCard}>
|
||||
<View style={styles.emptyState}>
|
||||
<Text style={styles.emptyIcon}>⏳</Text>
|
||||
<Text style={styles.emptyTitle}>A carregar espaços...</Text>
|
||||
</Card>
|
||||
</View>
|
||||
) : filtered.length === 0 ? (
|
||||
<Card style={styles.emptyCard}>
|
||||
<View style={styles.emptyState}>
|
||||
<Text style={styles.emptyIcon}>🔍</Text>
|
||||
<Text style={styles.emptyTitle}>Nenhuma barbearia encontrada</Text>
|
||||
<Text style={styles.emptyText}>Tente ajustar a pesquisa ou os filtros ativos.</Text>
|
||||
<Button variant="ghost" onPress={() => { setQuery(''); setFilter('todas'); }}>
|
||||
<Text style={styles.emptyText}>Tente ajustar a pesquisa ou filtros.</Text>
|
||||
<Button variant="outline" onPress={() => { setQuery(''); setFilter('todas'); }}>
|
||||
Limpar Tudo
|
||||
</Button>
|
||||
</Card>
|
||||
</View>
|
||||
) : (
|
||||
<View style={styles.list}>
|
||||
<Text style={styles.count}>{filtered.length} espaços disponíveis</Text>
|
||||
{filtered.map((shop) => (
|
||||
<Card key={shop.id} style={styles.shopCard}>
|
||||
<TouchableOpacity
|
||||
key={shop.id}
|
||||
style={styles.shopCard}
|
||||
activeOpacity={0.85}
|
||||
onPress={() => navigation.navigate('ShopDetails', { shopId: shop.id })}
|
||||
>
|
||||
{shop.imageUrl ? (
|
||||
<Image source={{ uri: shop.imageUrl }} style={styles.shopImage} />
|
||||
) : (
|
||||
<View style={styles.shopImageFallback}>
|
||||
<Text style={styles.shopImageFallbackText}>SA</Text>
|
||||
<Text style={styles.shopFallbackText}>{shop.name.charAt(0)}</Text>
|
||||
</View>
|
||||
)}
|
||||
<View style={styles.shopOverlay} />
|
||||
<View style={styles.shopBody}>
|
||||
<View style={styles.shopRow}>
|
||||
<Text style={styles.shopName} numberOfLines={1}>{shop.name}</Text>
|
||||
<Text style={styles.rating}>{(shop.rating || 0).toFixed(1)}</Text>
|
||||
<View style={styles.ratingPill}>
|
||||
<Text style={styles.ratingText}>★ {(shop.rating || 0).toFixed(1)}</Text>
|
||||
</View>
|
||||
</View>
|
||||
<Text style={styles.address} numberOfLines={1}>{shop.address || 'Endereço indisponível'}</Text>
|
||||
<View style={styles.metaRow}>
|
||||
<Text style={styles.metaText}>{(shop.services || []).length} serviços</Text>
|
||||
<Text style={styles.metaText}>{(shop.barbers || []).length} barbeiros</Text>
|
||||
<View style={styles.metaPill}>
|
||||
<Text style={styles.metaText}>{(shop.services || []).length} serviços</Text>
|
||||
</View>
|
||||
<View style={styles.metaPill}>
|
||||
<Text style={styles.metaText}>{(shop.barbers || []).length} barbeiros</Text>
|
||||
</View>
|
||||
</View>
|
||||
<Button style={styles.reserveButton} onPress={() => navigation.navigate('ShopDetails', { shopId: shop.id })}>
|
||||
Reservar
|
||||
</Button>
|
||||
</View>
|
||||
</Card>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</View>
|
||||
)}
|
||||
@@ -144,243 +135,184 @@ export default function Explore() {
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: '#f8fafc',
|
||||
backgroundColor: '#0a0a0f',
|
||||
},
|
||||
content: {
|
||||
padding: 16,
|
||||
gap: 18,
|
||||
},
|
||||
topBar: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
gap: 12,
|
||||
},
|
||||
brand: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 10,
|
||||
flex: 1,
|
||||
},
|
||||
brandIcon: {
|
||||
width: 34,
|
||||
height: 34,
|
||||
borderRadius: 10,
|
||||
backgroundColor: '#4f46e5',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
brandIconText: {
|
||||
color: '#fff',
|
||||
fontSize: 12,
|
||||
fontWeight: '900',
|
||||
},
|
||||
brandText: {
|
||||
color: '#0f172a',
|
||||
fontSize: 18,
|
||||
fontWeight: '900',
|
||||
textTransform: 'uppercase',
|
||||
},
|
||||
topActions: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 8,
|
||||
},
|
||||
cartButton: {
|
||||
minHeight: 36,
|
||||
borderRadius: 12,
|
||||
backgroundColor: '#f1f5f9',
|
||||
paddingHorizontal: 10,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
cartText: {
|
||||
color: '#334155',
|
||||
fontSize: 11,
|
||||
fontWeight: '900',
|
||||
},
|
||||
cartBadge: {
|
||||
position: 'absolute',
|
||||
right: -5,
|
||||
top: -5,
|
||||
minWidth: 18,
|
||||
textAlign: 'center',
|
||||
borderRadius: 999,
|
||||
backgroundColor: '#0f172a',
|
||||
color: '#818cf8',
|
||||
fontSize: 10,
|
||||
fontWeight: '900',
|
||||
paddingHorizontal: 4,
|
||||
},
|
||||
profileButton: {
|
||||
minWidth: 38,
|
||||
height: 38,
|
||||
borderRadius: 999,
|
||||
backgroundColor: '#0f172a',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
paddingHorizontal: 10,
|
||||
},
|
||||
profileText: {
|
||||
color: '#818cf8',
|
||||
fontSize: 12,
|
||||
fontWeight: '900',
|
||||
padding: 20,
|
||||
gap: 20,
|
||||
paddingBottom: 32,
|
||||
},
|
||||
header: {
|
||||
gap: 8,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'flex-start',
|
||||
},
|
||||
kicker: {
|
||||
alignSelf: 'flex-start',
|
||||
backgroundColor: '#e0e7ff',
|
||||
borderRadius: 999,
|
||||
paddingHorizontal: 10,
|
||||
paddingVertical: 5,
|
||||
},
|
||||
kickerText: {
|
||||
color: '#4338ca',
|
||||
fontSize: 10,
|
||||
fontWeight: '900',
|
||||
textTransform: 'uppercase',
|
||||
},
|
||||
title: {
|
||||
fontSize: 36,
|
||||
lineHeight: 38,
|
||||
fontWeight: '900',
|
||||
color: '#0f172a',
|
||||
textTransform: 'uppercase',
|
||||
},
|
||||
titleAccent: {
|
||||
color: '#4f46e5',
|
||||
},
|
||||
subtitle: {
|
||||
greeting: {
|
||||
color: '#64748b',
|
||||
fontSize: 15,
|
||||
lineHeight: 22,
|
||||
fontWeight: '500',
|
||||
},
|
||||
logoutButton: {
|
||||
alignSelf: 'flex-start',
|
||||
paddingVertical: 6,
|
||||
},
|
||||
logoutText: {
|
||||
color: '#e11d48',
|
||||
fontWeight: '800',
|
||||
},
|
||||
searchCard: {
|
||||
padding: 12,
|
||||
gap: 12,
|
||||
},
|
||||
searchInput: {
|
||||
minHeight: 48,
|
||||
borderRadius: 16,
|
||||
backgroundColor: '#f8fafc',
|
||||
color: '#0f172a',
|
||||
paddingHorizontal: 14,
|
||||
fontSize: 15,
|
||||
fontWeight: '600',
|
||||
marginBottom: 2,
|
||||
},
|
||||
headline: {
|
||||
color: '#f8fafc',
|
||||
fontSize: 32,
|
||||
fontWeight: '900',
|
||||
},
|
||||
brandPill: {
|
||||
width: 44,
|
||||
height: 44,
|
||||
borderRadius: 14,
|
||||
backgroundColor: '#141420',
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(99,102,241,0.25)',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
brandText: {
|
||||
color: '#818cf8',
|
||||
fontSize: 14,
|
||||
fontWeight: '900',
|
||||
},
|
||||
searchBox: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#141420',
|
||||
borderRadius: 16,
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(255,255,255,0.06)',
|
||||
paddingHorizontal: 16,
|
||||
gap: 10,
|
||||
},
|
||||
searchIcon: {
|
||||
color: '#475569',
|
||||
fontSize: 20,
|
||||
},
|
||||
searchInput: {
|
||||
flex: 1,
|
||||
height: 52,
|
||||
color: '#f8fafc',
|
||||
fontSize: 15,
|
||||
fontWeight: '500',
|
||||
},
|
||||
filters: {
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
gap: 8,
|
||||
},
|
||||
filterChip: {
|
||||
borderRadius: 999,
|
||||
backgroundColor: '#f1f5f9',
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 8,
|
||||
chip: {
|
||||
borderRadius: 12,
|
||||
backgroundColor: '#141420',
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 10,
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(255,255,255,0.04)',
|
||||
},
|
||||
filterChipActive: {
|
||||
backgroundColor: '#0f172a',
|
||||
chipActive: {
|
||||
backgroundColor: '#6366f1',
|
||||
borderColor: '#6366f1',
|
||||
},
|
||||
filterText: {
|
||||
color: '#475569',
|
||||
fontSize: 11,
|
||||
fontWeight: '900',
|
||||
chipText: {
|
||||
color: '#94a3b8',
|
||||
fontSize: 13,
|
||||
fontWeight: '700',
|
||||
},
|
||||
filterTextActive: {
|
||||
color: '#818cf8',
|
||||
chipTextActive: {
|
||||
color: '#fff',
|
||||
},
|
||||
list: {
|
||||
gap: 12,
|
||||
gap: 16,
|
||||
},
|
||||
count: {
|
||||
color: '#64748b',
|
||||
color: '#475569',
|
||||
fontSize: 12,
|
||||
fontWeight: '900',
|
||||
fontWeight: '700',
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: 0.5,
|
||||
},
|
||||
shopCard: {
|
||||
padding: 0,
|
||||
borderRadius: 22,
|
||||
overflow: 'hidden',
|
||||
backgroundColor: '#141420',
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(255,255,255,0.04)',
|
||||
},
|
||||
shopImage: {
|
||||
width: '100%',
|
||||
height: 150,
|
||||
height: 160,
|
||||
},
|
||||
shopImageFallback: {
|
||||
width: '100%',
|
||||
height: 150,
|
||||
backgroundColor: '#0f172a',
|
||||
height: 160,
|
||||
backgroundColor: '#1c1c2e',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
shopImageFallbackText: {
|
||||
color: '#818cf8',
|
||||
fontSize: 24,
|
||||
shopFallbackText: {
|
||||
color: '#6366f1',
|
||||
fontSize: 42,
|
||||
fontWeight: '900',
|
||||
opacity: 0.5,
|
||||
},
|
||||
shopOverlay: {},
|
||||
shopBody: {
|
||||
padding: 16,
|
||||
padding: 18,
|
||||
gap: 10,
|
||||
},
|
||||
shopRow: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
gap: 10,
|
||||
},
|
||||
shopName: {
|
||||
flex: 1,
|
||||
fontSize: 20,
|
||||
fontWeight: '900',
|
||||
color: '#0f172a',
|
||||
fontSize: 18,
|
||||
fontWeight: '800',
|
||||
color: '#f8fafc',
|
||||
},
|
||||
rating: {
|
||||
color: '#fff',
|
||||
backgroundColor: '#0f172a',
|
||||
borderRadius: 999,
|
||||
paddingHorizontal: 9,
|
||||
paddingVertical: 4,
|
||||
ratingPill: {
|
||||
backgroundColor: 'rgba(99,102,241,0.15)',
|
||||
borderRadius: 10,
|
||||
paddingHorizontal: 10,
|
||||
paddingVertical: 5,
|
||||
},
|
||||
ratingText: {
|
||||
color: '#a5b4fc',
|
||||
fontSize: 12,
|
||||
fontWeight: '900',
|
||||
fontWeight: '800',
|
||||
},
|
||||
address: {
|
||||
color: '#64748b',
|
||||
fontWeight: '500',
|
||||
fontSize: 14,
|
||||
},
|
||||
metaRow: {
|
||||
flexDirection: 'row',
|
||||
gap: 10,
|
||||
gap: 8,
|
||||
},
|
||||
metaPill: {
|
||||
backgroundColor: 'rgba(255,255,255,0.04)',
|
||||
borderRadius: 8,
|
||||
paddingHorizontal: 10,
|
||||
paddingVertical: 5,
|
||||
},
|
||||
metaText: {
|
||||
color: '#94a3b8',
|
||||
color: '#475569',
|
||||
fontSize: 11,
|
||||
fontWeight: '900',
|
||||
fontWeight: '700',
|
||||
textTransform: 'uppercase',
|
||||
},
|
||||
reserveButton: {
|
||||
backgroundColor: '#0f172a',
|
||||
marginTop: 4,
|
||||
},
|
||||
emptyCard: {
|
||||
padding: 28,
|
||||
emptyState: {
|
||||
alignItems: 'center',
|
||||
gap: 10,
|
||||
padding: 40,
|
||||
gap: 12,
|
||||
},
|
||||
emptyIcon: {
|
||||
fontSize: 48,
|
||||
marginBottom: 8,
|
||||
},
|
||||
emptyTitle: {
|
||||
color: '#0f172a',
|
||||
color: '#f8fafc',
|
||||
fontSize: 18,
|
||||
fontWeight: '900',
|
||||
fontWeight: '800',
|
||||
textAlign: 'center',
|
||||
},
|
||||
emptyText: {
|
||||
|
||||
Reference in New Issue
Block a user