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.
This commit is contained in:
@@ -0,0 +1,234 @@
|
||||
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,
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user