Skip to content

Commit 9627205

Browse files
authored
Merge pull request #15 from Equipe-Meta-Code/fix/bugs-sprint-2
Fix/bugs sprint 2
2 parents e04f20d + 96e9a95 commit 9627205

File tree

18 files changed

+639
-213
lines changed

18 files changed

+639
-213
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
app/src/services/api.ts

app/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
"@react-navigation/stack": "^7.2.2",
1818
"@reduxjs/toolkit": "^2.6.1",
1919
"axios": "^1.8.4",
20+
"dotenv": "^16.5.0",
2021
"expo": "~52.0.39",
22+
"expo-image-picker": "^16.0.6",
2123
"expo-status-bar": "~2.0.1",
2224
"react": "^18.3.1",
2325
"react-native": "0.76.7",

app/src/(redux)/appWrapper.tsx

Lines changed: 59 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
import React, { useEffect } from "react";
1+
import React, { useEffect, useState } from "react";
22
import { useDispatch } from "react-redux";
33
import { createStackNavigator } from "@react-navigation/stack";
44
import { loadUser } from "./authSlice"; // Ação para carregar o usuário
5+
import { View, ActivityIndicator } from "react-native";
6+
import { useSelector } from "react-redux";
7+
import { selectIsAuthenticated } from "./authSlice";
58

69
// Telas
710
import Home from "../pages/home/Home";
@@ -16,43 +19,66 @@ const Stack = createStackNavigator();
1619

1720
function AppWrapper() {
1821
const dispatch = useDispatch<AppDispatch>(); // Tipar o dispatch com AppDispatch
22+
const [loading, setLoading] = useState(true);
23+
const isAuthenticated = useSelector(selectIsAuthenticated);
1924

2025
useEffect(() => {
21-
// Quando a aplicação for carregada, tenta carregar o usuário do AsyncStorage
22-
dispatch(loadUser());
26+
const init = async () => {
27+
await dispatch(loadUser());
28+
setLoading(false);
29+
};
30+
init();
2331
}, [dispatch]);
2432

33+
if (loading) {
34+
return (
35+
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
36+
<ActivityIndicator size="large" color="#2A2F4F" />
37+
</View>
38+
);
39+
}
40+
2541
return (
26-
<Stack.Navigator>
27-
{/* Tela de Login */}
28-
<Stack.Screen
29-
name="Login"
30-
component={Login}
31-
options={{ title: "Login", headerShown: false }}
32-
/>
33-
<Stack.Screen
34-
name="Cadastro"
35-
component={Cadastro}
36-
options={{ title: "Cadastro", headerShown: false }}
37-
/>
38-
{/* Tela de Home (Possivelmente a tela inicial após login) */}
39-
<Stack.Screen
40-
name="Home"
41-
component={Home}
42-
options={{ title: "Home", headerShown: false }}
43-
/>
44-
{/* Tela de Profile */}
45-
<Stack.Screen
46-
name="Profile"
47-
component={Profile}
48-
options={{ title: "Profile" }}
49-
/>
50-
{/* Rota para o BottomRoutes, que é a navegação principal após login */}
51-
<Stack.Screen
52-
name="BottomRoutes"
53-
component={BottomRoutes}
54-
options={{ headerShown: false }} // Esconde o cabeçalho para as telas de Bottom Tab Navigator
55-
/>
42+
<Stack.Navigator
43+
initialRouteName={isAuthenticated ? "BottomRoutes" : "Login"}
44+
screenOptions={{ headerShown: false }}
45+
>
46+
{!isAuthenticated ? (
47+
<>
48+
{/* Tela de Login */}
49+
<Stack.Screen
50+
name="Login"
51+
component={Login}
52+
options={{ title: "Login", headerShown: false }}
53+
/>
54+
<Stack.Screen
55+
name="Cadastro"
56+
component={Cadastro}
57+
options={{ title: "Cadastro", headerShown: false }}
58+
/>
59+
</>
60+
) : (
61+
<>
62+
{/* Tela de Home (Possivelmente a tela inicial após login) */}
63+
<Stack.Screen
64+
name="Home"
65+
component={Home}
66+
options={{ title: "Home", headerShown: false }}
67+
/>
68+
{/* Tela de Profile */}
69+
<Stack.Screen
70+
name="Profile"
71+
component={Profile}
72+
options={{ title: "Profile" }}
73+
/>
74+
{/* Rota para o BottomRoutes, que é a navegação principal após login */}
75+
<Stack.Screen
76+
name="BottomRoutes"
77+
component={BottomRoutes}
78+
options={{ headerShown: false }} // Esconde o cabeçalho para as telas de Bottom Tab Navigator
79+
/>
80+
</>
81+
)}
5682
</Stack.Navigator>
5783
);
5884
};

app/src/(redux)/authSlice.tsx

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import AsyncStorage from "@react-native-async-storage/async-storage";
22
import { createSlice, PayloadAction, createAsyncThunk } from "@reduxjs/toolkit";
3+
import { RootState } from "./store";
34

45
// Definindo a estrutura do tipo User
56
interface User {
@@ -8,6 +9,7 @@ interface User {
89
name: string;
910
email: string;
1011
password: string;
12+
profileImage?: string;
1113
}
1214

1315
// Definindo o tipo do estado de autenticação
@@ -49,15 +51,34 @@ const authSlice = createSlice({
4951
setUserAction: (state, action: PayloadAction<User | null>) => {
5052
state.user = action.payload;
5153
},
54+
setProfileImage: (state, action: PayloadAction<string>) => {
55+
if (state.user) {
56+
state.user = { ...state.user, profileImage: action.payload };
57+
AsyncStorage.setItem("userInfo", JSON.stringify(state.user)).catch((error) =>
58+
console.error("Erro ao salvar imagem no AsyncStorage", error)
59+
);
60+
}
61+
},
5262
},
63+
extraReducers: (builder) => {
64+
builder.addCase(loadUser.fulfilled, (state, action: PayloadAction<User | null>) => {
65+
if (action.payload) {
66+
state.user = action.payload;
67+
}
68+
});
69+
}
5370
});
5471

5572
// Gerando as ações a partir do slice
56-
export const { loginUserAction, logoutAction, setUserAction } = authSlice.actions;
73+
export const { loginUserAction, logoutAction, setUserAction, setProfileImage } = authSlice.actions;
5774

5875
// Exportando o reducer
5976
export const authReducer = authSlice.reducer;
6077

78+
// Selector para saber se o usuário está autenticado
79+
export const selectIsAuthenticated = (state: RootState) =>
80+
Boolean(state.auth.user);
81+
6182
// Função thunk para carregar o usuário do AsyncStorage
6283
/* export const loadUser = () => async (dispatch: any) => {
6384
const userInfo = await loadUserFromStorage();

app/src/components/Input/index.tsx

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,59 @@
11
import React, { forwardRef, LegacyRef } from "react";
2-
import { TextInput, View, TextInputProps, Text, TouchableOpacity,StyleProp,TextStyle} from 'react-native';
2+
import { TextInput, View, TextInputProps, Text, TouchableOpacity, StyleProp, TextStyle } from 'react-native';
33
import { MaterialIcons, FontAwesome, Octicons } from '@expo/vector-icons';
4-
import { themas } from "../../global/themes";
54
import { style } from "./styles";
65

76
type IconComponent = React.ComponentType<React.ComponentProps<typeof MaterialIcons>> |
87
React.ComponentType<React.ComponentProps<typeof FontAwesome>> |
98
React.ComponentType<React.ComponentProps<typeof Octicons>>;
109

1110
type Props = TextInputProps & {
12-
IconLeft?: IconComponent,
13-
IconRigth?: IconComponent,
14-
iconLeftName?: string,
15-
iconRightName?: string,
16-
title?: string,
17-
onIconLeftPress?: () => void,
18-
onIconRigthPress?: () => void ,
19-
height?:number,
20-
labelStyle?:StyleProp<TextStyle>
21-
}
11+
IconLeft?: IconComponent;
12+
IconRigth?: IconComponent;
13+
iconLeftName?: string;
14+
iconRightName?: string;
15+
title?: string;
16+
onIconLeftPress?: () => void;
17+
onIconRigthPress?: () => void;
18+
height?: number;
19+
labelStyle?: StyleProp<TextStyle>;
20+
/** sinaliza erro para borda e texto */
21+
error?: boolean;
22+
};
2223

2324
export const Input = forwardRef((props: Props, ref: LegacyRef<TextInput> | null) => {
24-
const { IconLeft, IconRigth, iconLeftName, iconRightName, title, onIconLeftPress, onIconRigthPress, height,labelStyle,...rest } = props;
25-
25+
const {IconLeft, IconRigth, iconLeftName, iconRightName, title, onIconLeftPress, onIconRigthPress, height, labelStyle, error = false, ...rest} = props;
2626

2727
return (
2828
<>
2929
{title && <Text style={style.inputTitle}>{title}</Text>}
3030
<View style={style.inputContainer}>
31-
{IconLeft && iconLeftName && (
32-
<TouchableOpacity onPress={onIconLeftPress}>
33-
<IconLeft name={iconLeftName as any} size={20} color={'#888'} style={style.iconRight} />
34-
</TouchableOpacity>
35-
)}
31+
<View style={[
32+
style.inputWrapper,
33+
error && style.erroInput,
34+
height ? { height } : undefined
35+
]}>
36+
{IconLeft && iconLeftName && (
37+
<TouchableOpacity onPress={onIconLeftPress}>
38+
<IconLeft name={iconLeftName as any} size={20} color="#888" style={style.iconRight} />
39+
</TouchableOpacity>
40+
)}
3641

37-
<View style={style.inputWrapper}>
38-
<TextInput
39-
style={style.input}
42+
<TextInput
4043
ref={ref}
44+
style={style.input}
4145
multiline={rest.multiline ?? false}
4246
{...rest}
4347
/>
4448

4549
{IconRigth && iconRightName && (
4650
<TouchableOpacity onPress={onIconRigthPress}>
47-
<IconRigth name={iconRightName as any} size={24} color={'#888'} style={style.iconRight} />
51+
<IconRigth name={iconRightName as any} size={24} color="#888" style={style.iconRight} />
4852
</TouchableOpacity>
4953
)}
5054
</View>
55+
{error && <Text style={style.erroTexto}>Campo inválido</Text>}
5156
</View>
52-
5357
</>
5458
);
5559
});

app/src/components/Input/styles.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
import { StyleSheet,Dimensions} from "react-native";
1+
import { StyleSheet, Dimensions } from "react-native";
22

33

44
export const style = StyleSheet.create({
55
inputTitle: {
66
paddingTop: 5,
77
fontSize: 16,
88
color: 'black',
9-
marginTop: 20,
10-
alignSelf: 'flex-start',
11-
marginLeft: 10,
9+
marginTop: 20,
10+
alignSelf: 'flex-start',
11+
marginLeft: 10,
1212
},
1313
inputContainer: {
1414
width: '100%',
@@ -30,4 +30,13 @@ export const style = StyleSheet.create({
3030
iconRight: {
3131
marginLeft: 10,
3232
},
33-
});
33+
erroInput: {
34+
borderColor: 'red',
35+
},
36+
erroTexto: {
37+
color: 'red',
38+
fontSize: 12,
39+
marginTop: 4,
40+
marginBottom: 8,
41+
},
42+
});

app/src/components/customTabBar/index.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React from "react";
22
import { Text, TouchableOpacity, View } from "react-native";
33
import { BottomTabBarProps } from "@react-navigation/bottom-tabs";
44
import { style } from "./styles";
5+
import { themas } from "../../global/themes";
56
import {
67
FontAwesome5,
78
MaterialCommunityIcons,
@@ -20,6 +21,8 @@ const CustomTabBar: React.FC<BottomTabBarProps> = ({ state, descriptors, navigat
2021

2122
return (
2223
<View style={style.tabArea}>
24+
25+
{/* botão home */}
2326
<TouchableOpacity
2427
style={style.tabItemHome}
2528
onPress={() => go("Home")}
@@ -33,29 +36,38 @@ const CustomTabBar: React.FC<BottomTabBarProps> = ({ state, descriptors, navigat
3336
</Text>
3437
</TouchableOpacity>
3538

39+
{/* botão registrar */}
3640
<TouchableOpacity
3741
style={style.tabItemCenter}
3842
onPress={() => go("Registrar")}
3943
>
4044
<SimpleLineIcons
4145
name="plus"
42-
style={[style.iconCenter, isFocused("Registrar") && { color: "blue" }]}
46+
style={[style.iconCenter, isFocused("Registrar") && { color: themas.colors.primary }]}
4347
/>
4448
</TouchableOpacity>
4549

50+
{/* botão histórico */}
4651
<View style={style.tabItemRight}>
4752
<TouchableOpacity style={style.tabItem} onPress={() => go("Historico")}>
4853
<MaterialCommunityIcons
4954
name="file-document-edit"
50-
style={[style.iconRight, isFocused("Historico") && { color: "blue" }]}
55+
style={[style.iconRight, isFocused("Historico") && { color: themas.colors.primary }]}
5156
/>
57+
<Text style={[style.textRight, isFocused("Historico") && { color: themas.colors.primary }]}>
58+
Histórico
59+
</Text>
5260
</TouchableOpacity>
5361

62+
{/* botão perfil */}
5463
<TouchableOpacity style={style.tabItem} onPress={() => go("Perfil")}>
5564
<FontAwesome5
5665
name="user"
57-
style={[style.iconRight, isFocused("Perfil") && { color: "blue" }]}
66+
style={[style.iconRight, isFocused("Perfil") && { color: themas.colors.primary }]}
5867
/>
68+
<Text style={[style.textRight, isFocused("Perfil") && { color: themas.colors.primary }]}>
69+
Perfil
70+
</Text>
5971
</TouchableOpacity>
6072
</View>
6173
</View>

0 commit comments

Comments
 (0)