Skip to content

Commit 88fa1ff

Browse files
authored
Merge pull request #16 from Equipe-Meta-Code/dev
Dev
2 parents 931d015 + 9627205 commit 88fa1ff

File tree

41 files changed

+2345
-671
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+2345
-671
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

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
npm install
1414
```
1515
* Configurar o arquivo de API
16-
* Acesse o arquivo [api2.ts](./app/src/services/api2.ts)
16+
* Acesse o arquivo [api.ts](./app/src/services/api.ts)
1717
* Substitua ```<ip-da-sua-maquina>``` pelo IP correto da sua máquina.
1818

1919
* Inicie o frontend com o comando:

app/package-lock.json

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,17 @@
99
"web": "expo start --web"
1010
},
1111
"dependencies": {
12-
"@react-native-async-storage/async-storage": "1.23.1",
1312
"@expo/vector-icons": "^14.0.4",
13+
"@react-native-async-storage/async-storage": "1.23.1",
1414
"@react-native-community/datetimepicker": "8.2.0",
15+
"@react-native-picker/picker": "^2.11.0",
1516
"@react-navigation/bottom-tabs": "^7.3.2",
1617
"@react-navigation/stack": "^7.2.2",
1718
"@reduxjs/toolkit": "^2.6.1",
1819
"axios": "^1.8.4",
20+
"dotenv": "^16.5.0",
1921
"expo": "~52.0.39",
22+
"expo-image-picker": "^16.0.6",
2023
"expo-status-bar": "~2.0.1",
2124
"react": "^18.3.1",
2225
"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/api.ts

Lines changed: 0 additions & 7 deletions
This file was deleted.

app/src/components/Input/index.tsx

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import React, { forwardRef, LegacyRef } from "react";
2+
import { TextInput, View, TextInputProps, Text, TouchableOpacity, StyleProp, TextStyle } from 'react-native';
3+
import { MaterialIcons, FontAwesome, Octicons } from '@expo/vector-icons';
4+
import { style } from "./styles";
5+
6+
type IconComponent = React.ComponentType<React.ComponentProps<typeof MaterialIcons>> |
7+
React.ComponentType<React.ComponentProps<typeof FontAwesome>> |
8+
React.ComponentType<React.ComponentProps<typeof Octicons>>;
9+
10+
type Props = TextInputProps & {
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+
};
23+
24+
export const Input = forwardRef((props: Props, ref: LegacyRef<TextInput> | null) => {
25+
const {IconLeft, IconRigth, iconLeftName, iconRightName, title, onIconLeftPress, onIconRigthPress, height, labelStyle, error = false, ...rest} = props;
26+
27+
return (
28+
<>
29+
{title && <Text style={style.inputTitle}>{title}</Text>}
30+
<View style={style.inputContainer}>
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+
)}
41+
42+
<TextInput
43+
ref={ref}
44+
style={style.input}
45+
multiline={rest.multiline ?? false}
46+
{...rest}
47+
/>
48+
49+
{IconRigth && iconRightName && (
50+
<TouchableOpacity onPress={onIconRigthPress}>
51+
<IconRigth name={iconRightName as any} size={24} color="#888" style={style.iconRight} />
52+
</TouchableOpacity>
53+
)}
54+
</View>
55+
{error && <Text style={style.erroTexto}>Campo inválido</Text>}
56+
</View>
57+
</>
58+
);
59+
});

app/src/components/Input/styles.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { StyleSheet, Dimensions } from "react-native";
2+
3+
4+
export const style = StyleSheet.create({
5+
inputTitle: {
6+
paddingTop: 5,
7+
fontSize: 16,
8+
color: 'black',
9+
marginTop: 20,
10+
alignSelf: 'flex-start',
11+
marginLeft: 10,
12+
},
13+
inputContainer: {
14+
width: '100%',
15+
},
16+
inputWrapper: {
17+
flexDirection: 'row',
18+
alignItems: 'center',
19+
borderWidth: 1,
20+
borderColor: '#D3D3D3',
21+
borderRadius: 5,
22+
paddingHorizontal: 10,
23+
height: 40,
24+
marginTop: 5,
25+
},
26+
input: {
27+
flex: 1,
28+
color: 'black',
29+
},
30+
iconRight: {
31+
marginLeft: 10,
32+
},
33+
erroInput: {
34+
borderColor: 'red',
35+
},
36+
erroTexto: {
37+
color: 'red',
38+
fontSize: 12,
39+
marginTop: 4,
40+
marginBottom: 8,
41+
},
42+
});
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import React from "react";
2+
import { TouchableOpacity, Text } from "react-native";
3+
import { styles } from "./styles"; // Importa os estilos
4+
import { StyleProp, TextStyle, ViewStyle } from "react-native";
5+
6+
type Props = {
7+
title: string;
8+
onPress: () => void;
9+
buttonStyle?: StyleProp<ViewStyle>;
10+
loading?: boolean;
11+
textStyle?: StyleProp<TextStyle>;
12+
}
13+
14+
export function ButtonCustom({ title, onPress, buttonStyle, textStyle }: Props) {
15+
return (
16+
<TouchableOpacity
17+
style={[styles.button, buttonStyle]}
18+
onPress={onPress}
19+
activeOpacity={0.8}
20+
>
21+
<Text style={[styles.buttonText, textStyle]}>
22+
{title}
23+
</Text>
24+
</TouchableOpacity>
25+
);
26+
}

0 commit comments

Comments
 (0)