Files
MeowSpool/mobile/app/(app)/(tabs)/profile.tsx
T
Felipe d7fb768d3b feat: add domain models and repositories for user, spool presets, and filaments
- 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.
2026-03-14 10:36:13 -03:00

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,
},
});