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:
2026-03-14 10:36:13 -03:00
parent abdc2fe8ce
commit d7fb768d3b
76 changed files with 19681 additions and 0 deletions
+116
View File
@@ -0,0 +1,116 @@
import React, { useState } from 'react';
import { View, Text, StyleSheet, Alert } from 'react-native';
import { useLocalSearchParams, useRouter } from 'expo-router';
import { useForm, Controller } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { Ionicons } from '@expo/vector-icons';
import { Screen } from '@presentation/components/layout/Screen';
import { Header } from '@presentation/components/layout/Header';
import { Input } from '@presentation/components/ui/Input';
import { Button } from '@presentation/components/ui/Button';
import { colors, typography, spacing } from '@shared/theme';
const schema = z.object({
password: z.string().min(8, 'Mínimo 8 caracteres'),
confirmPassword: z.string(),
}).refine((d) => d.password === d.confirmPassword, {
message: 'As senhas não coincidem',
path: ['confirmPassword'],
});
type FormData = z.infer<typeof schema>;
/**
* Tela de Redefinir Senha — 117-0
* Recebe `token` via deep link: meowspool://reset-password?token=xxx
*/
export default function ResetPasswordScreen(): React.ReactElement {
const { token } = useLocalSearchParams<{ token: string }>();
const router = useRouter();
const [isLoading, setIsLoading] = useState(false);
const { control, handleSubmit, formState: { errors } } = useForm<FormData>({
resolver: zodResolver(schema),
defaultValues: { password: '', confirmPassword: '' },
});
async function onSubmit(data: FormData): Promise<void> {
if (!token) {
Alert.alert('Erro', 'Token inválido.');
return;
}
setIsLoading(true);
try {
// TODO: ResetPasswordUseCase
console.log('reset-password', { token, ...data });
router.replace('/(auth)/password-reset-done');
} catch {
Alert.alert('Erro', 'Não foi possível redefinir a senha.');
} finally {
setIsLoading(false);
}
}
return (
<Screen keyboardAvoiding>
<Header title="Nova senha" showBack />
<View style={styles.content}>
<Text style={styles.description}>
Escolha uma nova senha segura para sua conta.
</Text>
<Controller
control={control}
name="password"
render={({ field }) => (
<Input
label="NOVA SENHA"
placeholder="Mínimo 8 caracteres"
isPassword
leftIcon={<Ionicons name="lock-closed-outline" size={18} color={colors.textSecondary} />}
error={errors.password?.message}
onChangeText={field.onChange}
value={field.value}
/>
)}
/>
<Controller
control={control}
name="confirmPassword"
render={({ field }) => (
<Input
label="CONFIRMAR NOVA SENHA"
placeholder="Repita a senha"
isPassword
leftIcon={<Ionicons name="lock-closed-outline" size={18} color={colors.textSecondary} />}
error={errors.confirmPassword?.message}
onChangeText={field.onChange}
value={field.value}
/>
)}
/>
<Button label="Redefinir senha" onPress={handleSubmit(onSubmit)} isLoading={isLoading} style={styles.cta} />
</View>
</Screen>
);
}
const styles = StyleSheet.create({
content: {
flex: 1,
paddingTop: spacing[6],
gap: spacing[4],
},
description: {
fontFamily: typography.fontFamily.ui,
fontSize: typography.fontSize.base,
color: colors.textSecondary,
lineHeight: typography.fontSize.base * 1.6,
marginBottom: spacing[2],
},
cta: { marginTop: spacing[2] },
});