- Introduced SpoolPreset and User domain models with necessary DTOs and utility functions. - Created AuthRepository, FilamentRepository, and SpoolPresetRepository interfaces for authentication and data management. - Implemented UI components for filament display, including ColorSwatch, FilamentCard, and StockBar. - Developed layout components such as Header and Screen for consistent app structure. - Added reusable UI components like Badge, Button, Card, and Input for better user interaction. - Established global constants and theme settings for consistent styling across the application. - Implemented utility functions for filament calculations and formatting. - Created Zustand stores for managing authentication, filament, and preset states. - Configured TypeScript settings for improved development experience.
235 lines
7.6 KiB
TypeScript
235 lines
7.6 KiB
TypeScript
import React from 'react';
|
|
import {
|
|
View, Text, TouchableOpacity, StyleSheet, Alert,
|
|
} from 'react-native';
|
|
import { useRouter } from 'expo-router';
|
|
import { Ionicons } from '@expo/vector-icons';
|
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
import { useAuthStore } from '@store/authStore';
|
|
import { colors, typography, spacing, radius } from '@shared/theme';
|
|
|
|
/**
|
|
* Tela de Perfil — 1LQ-0
|
|
*/
|
|
export default function ProfileScreen(): React.ReactElement {
|
|
const router = useRouter();
|
|
const { user, clearSession } = useAuthStore();
|
|
|
|
function handleLogout(): void {
|
|
Alert.alert(
|
|
'Sair da conta',
|
|
'Deseja sair da sua conta? Seus dados offline continuarão disponíveis.',
|
|
[
|
|
{ text: 'Cancelar', style: 'cancel' },
|
|
{
|
|
text: 'Sair',
|
|
style: 'destructive',
|
|
onPress: async () => {
|
|
await clearSession();
|
|
router.replace('/(auth)/login');
|
|
},
|
|
},
|
|
],
|
|
);
|
|
}
|
|
|
|
const displayName = user?.name ?? 'Usuário';
|
|
const displayEmail = user?.email ?? 'email@exemplo.com';
|
|
const isGoogleLinked = user?.googleId != null;
|
|
|
|
return (
|
|
<SafeAreaView style={styles.safe}>
|
|
{/* Header */}
|
|
<View style={styles.header}>
|
|
<Text style={styles.headerTitle}>Perfil</Text>
|
|
</View>
|
|
|
|
<View style={styles.content}>
|
|
{/* Avatar */}
|
|
<View style={styles.avatarSection}>
|
|
<View style={styles.avatarRing}>
|
|
<Ionicons name="person-outline" size={40} color={colors.accent} />
|
|
</View>
|
|
<Text style={styles.userName}>{displayName}</Text>
|
|
<Text style={styles.userEmail}>{displayEmail}</Text>
|
|
{isGoogleLinked && (
|
|
<View style={styles.googleBadge}>
|
|
<Ionicons name="logo-google" size={14} color={colors.textPrimary} />
|
|
<Text style={styles.googleBadgeText}>Conectado com Google</Text>
|
|
</View>
|
|
)}
|
|
</View>
|
|
|
|
{/* Conta */}
|
|
<Text style={styles.sectionLabel}>CONTA</Text>
|
|
<View style={styles.menuGroup}>
|
|
<TouchableOpacity style={[styles.menuItem, styles.menuItemFirst]}>
|
|
<Ionicons name="mail-outline" size={20} color={colors.textSecondary} />
|
|
<View style={styles.menuItemContent}>
|
|
<Text style={styles.menuItemLabel}>E-mail</Text>
|
|
<Text style={styles.menuItemValue}>{displayEmail}</Text>
|
|
</View>
|
|
<Ionicons name="chevron-forward" size={18} color={colors.textSecondary} />
|
|
</TouchableOpacity>
|
|
<View style={styles.menuDivider} />
|
|
<TouchableOpacity style={[styles.menuItem, styles.menuItemLast]}>
|
|
<Ionicons name="lock-closed-outline" size={20} color={colors.textSecondary} />
|
|
<View style={styles.menuItemContent}>
|
|
<Text style={styles.menuItemLabel}>Senha</Text>
|
|
<Text style={styles.menuItemValue}>••••••••</Text>
|
|
</View>
|
|
<Ionicons name="chevron-forward" size={18} color={colors.textSecondary} />
|
|
</TouchableOpacity>
|
|
</View>
|
|
|
|
{/* Vinculações */}
|
|
<Text style={styles.sectionLabel}>VINCULAÇÕES</Text>
|
|
<View style={styles.menuGroup}>
|
|
<View style={[styles.menuItem, styles.menuItemFirst, styles.menuItemLast]}>
|
|
<Ionicons name="logo-google" size={20} color={colors.textSecondary} />
|
|
<View style={styles.menuItemContent}>
|
|
<Text style={styles.menuItemLabel}>Google</Text>
|
|
<Text style={styles.menuItemValue}>{displayEmail}</Text>
|
|
</View>
|
|
<View style={[styles.linkedBadge, !isGoogleLinked && styles.unlinkedBadge]}>
|
|
<Text style={[styles.linkedBadgeText, !isGoogleLinked && styles.unlinkedBadgeText]}>
|
|
{isGoogleLinked ? 'Vinculado' : 'Vincular'}
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
|
|
{/* Logout */}
|
|
<TouchableOpacity style={styles.logoutBtn} onPress={handleLogout}>
|
|
<Ionicons name="log-out-outline" size={18} color={colors.error} />
|
|
<Text style={styles.logoutText}>Sair da Conta</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
</SafeAreaView>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
safe: { flex: 1, backgroundColor: colors.bgBase },
|
|
header: {
|
|
paddingHorizontal: spacing[5],
|
|
paddingTop: spacing[5],
|
|
paddingBottom: spacing[4],
|
|
},
|
|
headerTitle: {
|
|
fontFamily: typography.fontFamily.ui,
|
|
fontSize: typography.fontSize.xl,
|
|
fontWeight: typography.fontWeight.bold,
|
|
color: colors.textPrimary,
|
|
},
|
|
content: { flex: 1, paddingHorizontal: spacing[5], gap: spacing[5] },
|
|
avatarSection: { alignItems: 'center', gap: spacing[2], paddingBottom: spacing[2] },
|
|
avatarRing: {
|
|
width: 80,
|
|
height: 80,
|
|
borderRadius: radius.full,
|
|
borderWidth: 2,
|
|
borderColor: colors.accent,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
backgroundColor: colors.bgSurface,
|
|
},
|
|
userName: {
|
|
fontFamily: typography.fontFamily.ui,
|
|
fontSize: typography.fontSize.lg,
|
|
fontWeight: typography.fontWeight.bold,
|
|
color: colors.textPrimary,
|
|
},
|
|
userEmail: {
|
|
fontFamily: typography.fontFamily.ui,
|
|
fontSize: typography.fontSize.sm,
|
|
color: colors.textSecondary,
|
|
},
|
|
googleBadge: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: 6,
|
|
backgroundColor: colors.bgSurface,
|
|
paddingHorizontal: spacing[3],
|
|
paddingVertical: 5,
|
|
borderRadius: radius.full,
|
|
borderWidth: 1,
|
|
borderColor: colors.border,
|
|
marginTop: spacing[1],
|
|
},
|
|
googleBadgeText: {
|
|
fontFamily: typography.fontFamily.ui,
|
|
fontSize: typography.fontSize.xs,
|
|
color: colors.textPrimary,
|
|
},
|
|
sectionLabel: {
|
|
fontFamily: typography.fontFamily.ui,
|
|
fontSize: typography.fontSize.xs,
|
|
fontWeight: typography.fontWeight.bold,
|
|
color: colors.textSecondary,
|
|
letterSpacing: 0.8,
|
|
textTransform: 'uppercase',
|
|
},
|
|
menuGroup: {
|
|
backgroundColor: colors.bgSurface,
|
|
borderRadius: radius.lg,
|
|
borderWidth: 1,
|
|
borderColor: colors.border,
|
|
overflow: 'hidden',
|
|
marginTop: -spacing[2],
|
|
},
|
|
menuItem: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: spacing[3],
|
|
padding: spacing[4],
|
|
},
|
|
menuItemFirst: {},
|
|
menuItemLast: {},
|
|
menuItemContent: { flex: 1 },
|
|
menuItemLabel: {
|
|
fontFamily: typography.fontFamily.ui,
|
|
fontSize: typography.fontSize.xs,
|
|
color: colors.textSecondary,
|
|
marginBottom: 2,
|
|
},
|
|
menuItemValue: {
|
|
fontFamily: typography.fontFamily.ui,
|
|
fontSize: typography.fontSize.base,
|
|
color: colors.textPrimary,
|
|
},
|
|
menuDivider: { height: 1, backgroundColor: colors.border, marginLeft: spacing[5] + 20 + spacing[3] },
|
|
linkedBadge: {
|
|
backgroundColor: colors.bgHover,
|
|
paddingHorizontal: spacing[3],
|
|
paddingVertical: 5,
|
|
borderRadius: radius.sm,
|
|
borderWidth: 1,
|
|
borderColor: colors.border,
|
|
},
|
|
unlinkedBadge: { borderColor: colors.accent, backgroundColor: colors.accentMuted },
|
|
linkedBadgeText: {
|
|
fontFamily: typography.fontFamily.ui,
|
|
fontSize: typography.fontSize.xs,
|
|
color: colors.textSecondary,
|
|
},
|
|
unlinkedBadgeText: { color: colors.accent },
|
|
logoutBtn: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
gap: spacing[2],
|
|
backgroundColor: 'rgba(255,107,107,0.12)',
|
|
paddingVertical: spacing[4],
|
|
borderRadius: radius.lg,
|
|
borderWidth: 1,
|
|
borderColor: 'rgba(255,107,107,0.3)',
|
|
},
|
|
logoutText: {
|
|
fontFamily: typography.fontFamily.ui,
|
|
fontSize: typography.fontSize.base,
|
|
fontWeight: typography.fontWeight.medium,
|
|
color: colors.error,
|
|
},
|
|
});
|