Implementação do RoadtripDJ - Funcionalidades e UI

This commit is contained in:
Eduardo Silva
2026-05-15 12:26:05 +01:00
parent 846525d2e3
commit 56c2f20c11
22 changed files with 3013 additions and 17 deletions

View File

@@ -0,0 +1,285 @@
import React from 'react';
import { View, Text, StyleSheet, SafeAreaView, TouchableOpacity, TextInput, ImageBackground, KeyboardAvoidingView, Platform, ScrollView } from 'react-native';
import { X, MapPin, ArrowRight } from 'lucide-react-native';
import { colors } from '../../utils/colors';
// @ts-ignore
export default function NewTripScreen({ navigation }) {
const handleCreateRoute = () => {
// In a real app, this would trigger the orchestration flow (Ollama -> Spotify -> Firebase)
// For now, we mock success and navigate to the Trip Details
navigation.replace('TripDetails');
};
return (
<SafeAreaView style={styles.safeArea}>
<KeyboardAvoidingView
style={styles.container}
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
>
{/* Header */}
<View style={styles.header}>
<Text style={styles.title}>Nova Viagem</Text>
<TouchableOpacity
style={styles.closeButton}
onPress={() => navigation.goBack()}
>
<X color={colors.textMain} size={20} />
</TouchableOpacity>
</View>
<ScrollView contentContainerStyle={styles.scrollContent}>
{/* Map Area Placeholder */}
<View style={styles.mapArea}>
{/* Using a solid light gray color instead of a complex map image to keep it clean */}
<View style={styles.mockRouteVisual}>
<View style={styles.routeDotLarge} />
<View style={styles.routeLineDashed} />
<View style={styles.routePinLarge}>
<MapPin color={colors.white} size={14} />
</View>
</View>
</View>
{/* Form Card */}
<View style={styles.formCard}>
<View style={styles.inputGroup}>
<Text style={styles.inputLabel}>NOME DA VIAGEM</Text>
<TextInput
style={styles.textInput}
placeholder="Ex: Fim de semana no Algarve"
placeholderTextColor={colors.textSecondary}
/>
</View>
<View style={styles.routeInputContainer}>
{/* Visual timeline on the left */}
<View style={styles.routeTimeline}>
<View style={styles.timelineDot} />
<View style={styles.timelineLine} />
<MapPin color={colors.textSecondary} size={16} style={styles.timelinePin} />
</View>
<View style={styles.routeInputs}>
<View style={styles.inputGroup}>
<Text style={styles.inputLabel}>PARTIDA</Text>
<TextInput
style={[styles.textInput, styles.routeTextInput]}
value="Lisboa, Portugal"
placeholderTextColor={colors.textMain}
/>
</View>
<View style={[styles.inputGroup, { marginBottom: 0 }]}>
<Text style={styles.inputLabel}>DESTINO</Text>
<TextInput
style={[styles.textInput, styles.routeTextInput]}
value="Porto, Portugal"
placeholderTextColor={colors.textMain}
/>
</View>
</View>
</View>
</View>
{/* Bottom Actions */}
<View style={styles.bottomActions}>
<TouchableOpacity
style={styles.primaryButton}
onPress={handleCreateRoute}
>
<Text style={styles.primaryButtonText}>Criar Rota & Playlist</Text>
<ArrowRight color={colors.white} size={20} />
</TouchableOpacity>
<Text style={styles.disclaimerText}>
A IA vai analisar o trajeto, pontos de interesse, clima{'\n'}e duração para criar a banda sonora perfeita.
</Text>
</View>
</ScrollView>
</KeyboardAvoidingView>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
safeArea: {
flex: 1,
backgroundColor: colors.white,
},
container: {
flex: 1,
},
scrollContent: {
flexGrow: 1,
},
header: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingHorizontal: 20,
paddingTop: 16,
paddingBottom: 16,
backgroundColor: colors.white,
zIndex: 10,
},
title: {
fontSize: 22,
fontWeight: 'bold',
color: colors.textMain,
},
closeButton: {
width: 36,
height: 36,
borderRadius: 18,
backgroundColor: colors.inputBackground,
justifyContent: 'center',
alignItems: 'center',
},
mapArea: {
height: 180,
backgroundColor: '#F0F2F5', // Light map-like gray
justifyContent: 'center',
alignItems: 'center',
},
mockRouteVisual: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: 40,
width: '100%',
},
routeDotLarge: {
width: 24,
height: 24,
borderRadius: 12,
backgroundColor: colors.primary,
borderWidth: 4,
borderColor: colors.white,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
zIndex: 2,
},
routeLineDashed: {
flex: 1,
height: 4,
borderWidth: 2,
borderColor: colors.primary,
borderStyle: 'dashed',
marginHorizontal: -4, // Overlap slightly
zIndex: 1,
},
routePinLarge: {
width: 28,
height: 28,
borderRadius: 14,
backgroundColor: '#000000',
justifyContent: 'center',
alignItems: 'center',
borderWidth: 2,
borderColor: colors.white,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
zIndex: 2,
},
formCard: {
backgroundColor: colors.white,
borderTopLeftRadius: 32,
borderTopRightRadius: 32,
padding: 24,
marginTop: -32, // Overlap the map
shadowColor: '#000',
shadowOffset: { width: 0, height: -4 },
shadowOpacity: 0.05,
shadowRadius: 12,
elevation: 10,
},
inputGroup: {
marginBottom: 20,
},
inputLabel: {
fontSize: 12,
fontWeight: 'bold',
color: colors.textSecondary,
marginBottom: 8,
letterSpacing: 0.5,
},
textInput: {
backgroundColor: colors.inputBackground,
borderRadius: 16,
paddingHorizontal: 16,
paddingVertical: 16,
fontSize: 16,
color: colors.textMain,
fontWeight: '500',
},
routeInputContainer: {
flexDirection: 'row',
marginTop: 10,
},
routeTimeline: {
alignItems: 'center',
width: 30,
marginTop: 38, // Align with inputs
marginRight: 8,
},
timelineDot: {
width: 10,
height: 10,
borderRadius: 5,
backgroundColor: colors.primary,
},
timelineLine: {
width: 1,
height: 60,
backgroundColor: colors.inputBorder,
marginVertical: 4,
},
timelinePin: {
marginTop: 4,
},
routeInputs: {
flex: 1,
},
routeTextInput: {
fontWeight: 'bold',
},
bottomActions: {
paddingHorizontal: 24,
paddingBottom: 40,
backgroundColor: colors.white,
},
primaryButton: {
backgroundColor: colors.primary,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
paddingVertical: 18,
borderRadius: 16,
marginBottom: 16,
shadowColor: colors.primary,
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.3,
shadowRadius: 8,
elevation: 6,
},
primaryButtonText: {
color: colors.white,
fontSize: 18,
fontWeight: 'bold',
marginRight: 8,
},
disclaimerText: {
textAlign: 'center',
color: colors.textSecondary,
fontSize: 12,
lineHeight: 18,
fontWeight: '500',
},
});