Página de perfil e página de alterar perfil

This commit is contained in:
Matheus Albino Brunhara
2022-05-19 19:01:04 -05:00
parent 453c763f6d
commit a19ab7b85f
7 changed files with 213 additions and 66 deletions

42
package-lock.json generated
View File

@@ -27,6 +27,7 @@
"@types/react-router-dom": "^5.1.7", "@types/react-router-dom": "^5.1.7",
"axios": "^0.26.1", "axios": "^0.26.1",
"ionicons": "^5.4.0", "ionicons": "^5.4.0",
"lodash.isequal": "^4.5.0",
"react": "^17.0.1", "react": "^17.0.1",
"react-dom": "^17.0.1", "react-dom": "^17.0.1",
"react-hook-form": "^7.30.0", "react-hook-form": "^7.30.0",
@@ -50,6 +51,7 @@
"devDependencies": { "devDependencies": {
"@capacitor/cli": "3.4.3", "@capacitor/cli": "3.4.3",
"@types/axios": "^0.14.0", "@types/axios": "^0.14.0",
"@types/lodash.isequal": "^4.5.6",
"react-scripts": "5.0.1" "react-scripts": "5.0.1"
} }
}, },
@@ -3829,6 +3831,21 @@
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
"dev": true "dev": true
}, },
"node_modules/@types/lodash": {
"version": "4.14.182",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz",
"integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==",
"dev": true
},
"node_modules/@types/lodash.isequal": {
"version": "4.5.6",
"resolved": "https://registry.npmjs.org/@types/lodash.isequal/-/lodash.isequal-4.5.6.tgz",
"integrity": "sha512-Ww4UGSe3DmtvLLJm2F16hDwEQSv7U0Rr8SujLUA2wHI2D2dm8kPu6Et+/y303LfjTIwSBKXB/YTUcAKpem/XEg==",
"dev": true,
"dependencies": {
"@types/lodash": "*"
}
},
"node_modules/@types/mime": { "node_modules/@types/mime": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
@@ -10665,6 +10682,11 @@
"integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=",
"dev": true "dev": true
}, },
"node_modules/lodash.isequal": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
"integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
},
"node_modules/lodash.memoize": { "node_modules/lodash.memoize": {
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
@@ -19387,6 +19409,21 @@
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
"dev": true "dev": true
}, },
"@types/lodash": {
"version": "4.14.182",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz",
"integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==",
"dev": true
},
"@types/lodash.isequal": {
"version": "4.5.6",
"resolved": "https://registry.npmjs.org/@types/lodash.isequal/-/lodash.isequal-4.5.6.tgz",
"integrity": "sha512-Ww4UGSe3DmtvLLJm2F16hDwEQSv7U0Rr8SujLUA2wHI2D2dm8kPu6Et+/y303LfjTIwSBKXB/YTUcAKpem/XEg==",
"dev": true,
"requires": {
"@types/lodash": "*"
}
},
"@types/mime": { "@types/mime": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
@@ -24566,6 +24603,11 @@
"integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=",
"dev": true "dev": true
}, },
"lodash.isequal": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
"integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
},
"lodash.memoize": { "lodash.memoize": {
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",

View File

@@ -22,6 +22,7 @@
"@types/react-router-dom": "^5.1.7", "@types/react-router-dom": "^5.1.7",
"axios": "^0.26.1", "axios": "^0.26.1",
"ionicons": "^5.4.0", "ionicons": "^5.4.0",
"lodash.isequal": "^4.5.0",
"react": "^17.0.1", "react": "^17.0.1",
"react-dom": "^17.0.1", "react-dom": "^17.0.1",
"react-hook-form": "^7.30.0", "react-hook-form": "^7.30.0",
@@ -69,6 +70,7 @@
"devDependencies": { "devDependencies": {
"@capacitor/cli": "3.4.3", "@capacitor/cli": "3.4.3",
"@types/axios": "^0.14.0", "@types/axios": "^0.14.0",
"@types/lodash.isequal": "^4.5.6",
"react-scripts": "5.0.1" "react-scripts": "5.0.1"
}, },
"description": "An Ionic project" "description": "An Ionic project"

View File

@@ -30,6 +30,7 @@ import '@ionic/react/css/display.css';
/* Theme variables */ /* Theme variables */
import './theme/variables.css'; import './theme/variables.css';
import Perfil from './pages/Perfil'; import Perfil from './pages/Perfil';
import PerfilEditar from './pages/PerfilEditar';
setupIonicReact(); setupIonicReact();
@@ -42,7 +43,7 @@ const App: React.FC = () => (
<Route exact path="/cadastro" component={Cadastro}></Route> <Route exact path="/cadastro" component={Cadastro}></Route>
<Route exact path="/login" component={Login}></Route> <Route exact path="/login" component={Login}></Route>
<Route exact path="/perfil" component={Perfil}></Route> <Route exact path="/perfil" component={Perfil}></Route>
{/* <Route exact path="/perfil/edit" component={Perfil}></Route> TODO */} <Route exact path="/perfil/editar" component={PerfilEditar}></Route>
<Route exact path="/"> <Route exact path="/">
<Redirect to="/cadastro" /> <Redirect to="/cadastro" />
</Route> </Route>

View File

@@ -5,6 +5,9 @@ const usersRoutes = {
}, },
get: { get: {
url: `${usersRoutesDefault}` url: `${usersRoutesDefault}`
},
update: {
url: `${usersRoutesDefault}/edit`
} }
} }

View File

@@ -31,64 +31,63 @@ const Perfil: React.FC = () => {
const [inputValues, setInputValues] = useReducer( const [inputValues, setInputValues] = useReducer(
(state: any, newState: any) => ({ ...state, ...newState }), (state: any, newState: any) => ({ ...state, ...newState }),
{ {
fullname: '', name: '',
bio: '' bio: '',
email: ''
} }
); );
const history = useHistory(); const history = useHistory();
const loadUserData = async () => {
let userId = ''
await sessionRoutes.refresh().then(response => {
if (response.status === 'error') {
setMessageToast(response.message);
setShowToast(true);
return
}
userId = response.userId
}).catch(error => {
console.dir('Houve um erro: ', { error })
alert('Houve um erro')
})
await usersRoutes.getById(userId).then(response => {
if (response.status === 'error') {
setMessageToast(response.message.data)
setShowToast(true);
return
}
const userData = response.data
setInputValues({'fullname': userData.name, 'bio': userData.bio});
}).catch(error => {
console.dir('Houve um erro: ', { error })
alert('Houve um erro')
})
}
useEffect(() => { useEffect(() => {
const userToken = LocalStorage.getToken() const redirectUserToLogin = () => {
if (!userToken) {
// TODO, não impede o usuário de retornar a página de login // TODO, não impede o usuário de retornar a página de login
history.push({ pathname: '/login' }); history.push({ pathname: '/login' });
setMessageToast("Por favor, autentique-se!"); setMessageToast("Por favor, autentique-se!");
setShowToast(true); setShowToast(true);
} }
const loadUserData = async () => {
let userId = ''
await sessionRoutes.refresh().then(response => {
if (response.status === 'error') {
setMessageToast(response.message);
setShowToast(true);
return
}
userId = response.userId
}).catch(() => {
redirectUserToLogin()
})
await usersRoutes.getById(userId).then(response => {
if (response.status === 'error') {
setMessageToast(response.message.data)
setShowToast(true);
return
}
const userData = response.data
setInputValues({'name': userData.name, 'bio': userData.bio, 'email': userData.email});
}).catch(() => {
redirectUserToLogin()
})
}
const userToken = LocalStorage.getToken()
if (!userToken) {
redirectUserToLogin()
}
// const refreshResponse = refreshSession() // const refreshResponse = refreshSession()
loadUserData() loadUserData()
}, []); }, [history]);
const handleEditProfile = () => {
alert('oi')
}
return ( return (
<IonPage> <IonPage>
@@ -116,7 +115,7 @@ const Perfil: React.FC = () => {
</IonRow> </IonRow>
<IonCardHeader> <IonCardHeader>
<IonCardTitle class="ion-text-center">{inputValues.fullname}</IonCardTitle> <IonCardTitle class="ion-text-center">{inputValues.name}</IonCardTitle>
</IonCardHeader> </IonCardHeader>
</IonCardContent> </IonCardContent>
</IonCard> </IonCard>
@@ -130,8 +129,8 @@ const Perfil: React.FC = () => {
</IonCardContent> </IonCardContent>
</IonCard> </IonCard>
<IonFab vertical="bottom" horizontal="end" slot="fixed" onClick={handleEditProfile}> <IonFab vertical="bottom" horizontal="end" slot="fixed">
<IonFabButton> <IonFabButton onClick={() => history.push({ pathname: '/perfil/editar', state: { userData: inputValues } })}>
<IonIcon icon={createOutline} /> <IonIcon icon={createOutline} />
</IonFabButton> </IonFabButton>
</IonFab> </IonFab>

View File

@@ -1,8 +1,6 @@
import { import {
IonCard, IonBackButton,
IonCardContent, IonButtons,
IonCardHeader,
IonCardTitle,
IonContent, IonContent,
IonFab, IonFab,
IonFabButton, IonFabButton,
@@ -13,20 +11,77 @@ import {
IonItem, IonItem,
IonLabel, IonLabel,
IonPage, IonPage,
IonTextarea,
IonTitle, IonTitle,
IonToast,
IonToolbar IonToolbar
} from "@ionic/react"; } from "@ionic/react";
import React, { useState } from "react"; import React, { useEffect, useReducer, useState } from "react";
import { IonRow, IonCol } from "@ionic/react"; import { IonRow, IonCol } from "@ionic/react";
import { createOutline } from "ionicons/icons";
import './Perfil.css' import './Perfil.css'
import { useHistory, useLocation } from "react-router";
import { saveOutline } from "ionicons/icons";
const Perfil: React.FC = () => { import isEqual from 'lodash.isequal';
const [email, setEmail] = useState<string>("matheusalb3213@gmail.com");
const handleEditProfile = () => { import * as usersRoutes from '../services/users';
alert('oi')
interface userData {
name: string;
bio: string;
email: string;
}
interface LocationState {
userData: userData
}
const PerfilEditar: React.FC = () => {
const history = useHistory();
const location = useLocation<LocationState>();
const [showToast, setShowToast] = useState(false);
const [messageToast, setMessageToast] = useState('');
const [userData, setUserData] = useState({
name: '',
bio: '',
email: ''
});
const [inputValues, setInputValues] = useReducer(
(state: any, newState: any) => ({ ...state, ...newState }),
{
name: '',
bio: '',
email: ''
}
);
useEffect(() => {
setUserData(location.state.userData)
setInputValues({'name': userData.name, 'email': userData.email, 'bio': userData.bio});
}, [userData]);
const handleUpdateUserData = () => {
usersRoutes.update(inputValues).then(response => {
if (response.status === 'error') {
setMessageToast(response.message);
setShowToast(true);
return
}
console.log(response)
}).catch((err) => {
setMessageToast(err);
setShowToast(true);
})
}
const hasChangedSinceInitialState = () => {
return isEqual(userData, inputValues)
} }
return ( return (
@@ -34,6 +89,9 @@ const Perfil: React.FC = () => {
<IonHeader> <IonHeader>
<IonToolbar> <IonToolbar>
<IonTitle>Editar perfil</IonTitle> <IonTitle>Editar perfil</IonTitle>
<IonButtons slot="start">
<IonBackButton defaultHref="perfil" />
</IonButtons>
</IonToolbar> </IonToolbar>
</IonHeader> </IonHeader>
@@ -48,19 +106,48 @@ const Perfil: React.FC = () => {
<IonRow> <IonRow>
<IonCol> <IonCol>
<IonItem> <IonItem>
<IonLabel position="floating"> Email</IonLabel> <IonLabel position="stacked"> Nome completo</IonLabel>
<IonInput
type="text"
value={inputValues.name}
onIonChange={(e) => setInputValues({'name': e.detail.value!})}
></IonInput>
</IonItem>
<IonItem>
<IonLabel position="stacked"> Email</IonLabel>
<IonInput <IonInput
type="email" type="email"
value={email} value={inputValues.email}
onIonChange={(e) => setEmail(e.detail.value!)} onIonChange={(e) => setInputValues({'email': e.detail.value!})}
></IonInput> ></IonInput>
</IonItem> </IonItem>
<IonItem>
<IonLabel position="stacked"> Biografia</IonLabel>
<IonTextarea
value={inputValues.bio}
onIonChange={(e) => setInputValues({'bio': e.detail.value!})}
></IonTextarea>
</IonItem>
</IonCol> </IonCol>
</IonRow> </IonRow>
</IonGrid> </IonGrid>
<IonFab vertical="bottom" horizontal="end" slot="fixed">
<IonFabButton disabled={hasChangedSinceInitialState()} onClick={handleUpdateUserData}>
<IonIcon icon={saveOutline} />
</IonFabButton>
</IonFab>
<IonToast
color='danger'
isOpen={showToast}
onDidDismiss={() => setShowToast(false)}
message={messageToast}
duration={2500}
/>
</IonContent> </IonContent>
</IonPage> </IonPage>
); );
}; };
export default Perfil; export default PerfilEditar;

View File

@@ -29,10 +29,16 @@ export interface CadastroResponse {
} }
export interface CadastroRequest { export interface CadastroRequest {
name: string; name: string;
email: string; email: string;
birth_date: string; birth_date: string;
password: string; password: string;
}
export interface UpdateUserRequest {
name: string;
email: string;
bio: string;
} }
// export async function get(cpf) { // export async function get(cpf) {
@@ -54,4 +60,11 @@ export async function getById(userId: string) {
const response = await instance.get(userRoutes.get.url + `/${userId}`, { headers: header }); const response = await instance.get(userRoutes.get.url + `/${userId}`, { headers: header });
return response.data; return response.data;
}
export async function update(userData: UpdateUserRequest) {
updateHeader();
const response = await instance.patch(userRoutes.update.url, userData, { headers: header });
return response.data;
} }