Terminando telas de completar cadastro

This commit is contained in:
Matheus Albino Brunhara
2022-06-20 04:05:34 -05:00
parent c368324747
commit 3e63a74fc9
10 changed files with 411 additions and 151 deletions

View File

@@ -19,7 +19,8 @@ import Perfil from './pages/Perfil';
import PerfilEditar from './pages/PerfilEditar'; import PerfilEditar from './pages/PerfilEditar';
import CadastroVan from './pages/CadastroVan'; import CadastroVan from './pages/CadastroVan';
import CadastroCompletar from './pages/CadastroCompletar/CadastroCompletar'; import CadastroCompletar from './pages/CadastroCompletar/CadastroCompletar';
import CompletarDocumentos from './pages/CadastroCompletar/CompletarDocumentos'; import CompletarDocumento from './pages/CadastroCompletar/CompletarDocumento';
import CompletarTelefone from './pages/CadastroCompletar/CompletarTelefone';
/* Core CSS required for Ionic components to work properly */ /* Core CSS required for Ionic components to work properly */
import '@ionic/react/css/core.css'; import '@ionic/react/css/core.css';
@@ -55,7 +56,8 @@ const routes = (
<Route exact path="/perfil" component={Perfil}></Route> <Route exact path="/perfil" component={Perfil}></Route>
<Route exact path="/perfil/editar" component={PerfilEditar}></Route> <Route exact path="/perfil/editar" component={PerfilEditar}></Route>
<Route exact path="/perfil/completar" component={CadastroCompletar}></Route> <Route exact path="/perfil/completar" component={CadastroCompletar}></Route>
<Route exact path="/perfil/completar/documentos" component={CompletarDocumentos}></Route> <Route exact path="/perfil/completar/documento" component={CompletarDocumento}></Route>
<Route exact path="/perfil/completar/telefone" component={CompletarTelefone}></Route>
<Route exact path="/usuario/:id" component={Perfil}></Route> <Route exact path="/usuario/:id" component={Perfil}></Route>

View File

@@ -12,13 +12,11 @@ IonPage,
IonTitle, IonTitle,
IonToolbar IonToolbar
} from "@ionic/react"; } from "@ionic/react";
import React, { useEffect, useReducer, useState } from "react"; import React, { useEffect, useReducer } from "react";
import '../Perfil.css' import '../Perfil.css'
import { useHistory, useLocation } from "react-router"; import { useHistory, useLocation } from "react-router";
import { bookOutline, callOutline, documentTextOutline, homeOutline } from "ionicons/icons"; import { callOutline, documentTextOutline } from "ionicons/icons";
import * as usersRoutes from '../../services/api/users';
import '../Cadastro/Cadastro.css' import '../Cadastro/Cadastro.css'
@@ -26,93 +24,60 @@ interface userData {
name: string; name: string;
lastname: string; lastname: string;
email: string; email: string;
phone_number: string;
birth_date: string; birth_date: string;
bio: string; bio: string;
document_type: string;
document: string;
} }
interface LocationState { interface LocationState {
userData: userData userData: userData
} }
const items = [ interface cardItem {
// TODO, CPF e CNH icon: string;
label: string;
description: string;
url: string;
required: boolean;
}
let items: cardItem[] = [
{ {
icon: documentTextOutline, icon: documentTextOutline,
label: 'Documentos', label: 'Documento',
description: 'Cadastre seus documentos para que seu perfil possa ser verificado.' description: 'Cadastre seu documento para que seu perfil possa ser verificado',
url: '/perfil/completar/documento',
required: false
}, },
// TODO, telefone e WhatsApp
{ {
icon: callOutline, icon: callOutline,
label: 'Informações de contato', label: 'Informações de contato',
description: 'Cadastre seu número de telefone celular que para possam contatar você.' description: 'Cadastre seu número de telefone celular que para possam contatar você',
}, url: '/perfil/completar/telefone',
{ required: false
icon: homeOutline, }
label: 'Endereço de residência',
description: 'Diga-nos seu endereço para que possa começar a solicitar vagas.'
},
{
icon: bookOutline,
label: 'Instituição de ensino',
description: 'Diga-nos sua IES para que possa começar a solicitar vagas.'
},
] ]
const CadastroCompletar: React.FC = () => { const CadastroCompletar: React.FC = () => {
const history = useHistory(); const history = useHistory();
const location = useLocation<LocationState>(); const location = useLocation<LocationState>();
const [showToast, setShowToast] = useState(false); const handleCardClick = (item: cardItem) => {
const [messageToast, setMessageToast] = useState(''); if (!item.required) return
const [userData, setUserData] = useState({ history.push({ pathname: item.url });
name: '', }
lastname: '',
email: '',
birth_date: '',
bio: '',
});
const [inputValues, setInputValues] = useReducer(
(state: any, newState: any) => ({ ...state, ...newState }),
{
name: '',
lastname: '',
email: '',
birth_date: '',
bio: '',
}
);
useEffect(() => { useEffect(() => {
let userData = location.state.userData if (!location.state) {
history.push({ pathname: '/perfil' })
}
setUserData(location.state.userData) if (!location.state.userData.document) items[0].required = true
setInputValues({ if (!location.state.userData.phone_number) items[1].required = true
'name': userData.name, }, []);
'lastname': userData.lastname,
'email': userData.email,
'birth_date': userData.birth_date,
'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);
})
}
return ( return (
<IonPage> <IonPage>
@@ -128,7 +93,7 @@ const CadastroCompletar: React.FC = () => {
<IonContent> <IonContent>
{ items.map((item, index) => { { items.map((item, index) => {
return ( return (
<IonCard button key={index}> <IonCard button={item.required} key={index} onClick={() => { handleCardClick(item) }}>
<IonItem> <IonItem>
<IonIcon icon={item.icon} slot="start" /> <IonIcon icon={item.icon} slot="start" />
<IonLabel>{item.label}</IonLabel> <IonLabel>{item.label}</IonLabel>

View File

@@ -0,0 +1,193 @@
import {
IonBackButton,
IonButtons,
IonContent,
IonFab,
IonFabButton,
IonHeader,
IonIcon,
IonList,
IonPage,
IonSelect,
IonSelectOption,
IonTitle,
IonToast,
IonToolbar
} from "@ionic/react";
import React, { useEffect, useState } from "react";
import { IonGrid, IonRow, IonCol } from "@ionic/react";
import { useHistory, useLocation } from "react-router-dom";
import {
IonItem,
IonLabel,
IonInput,
} from "@ionic/react";
import { saveOutline } from "ionicons/icons";
import * as usersRoutes from '../../services/api/users';
import validateCpf from '../../services/validateCpf'
interface documentTypesInterface {
label: string;
name: string;
}
const CompletarDocumento: React.FC = () => {
const [hasChangedSinceInitialState, setHasChangedSinceInitialState] = useState(false);
const [documentTypes, setDocumentTypes] = useState<documentTypesInterface[]>([]);
const [document, setDocument] = useState('');
const [documentType, setDocumentType] = useState('');
const [documentMaxLength, setDocumentMaxLength] = useState(0);
const [showToast, setShowToast] = useState(false);
const [messageToast, setMessageToast] = useState('');
const history = useHistory();
const location = useLocation();
useEffect(() => {
if (!location.state) {
history.push({ pathname: '/perfil/completar' })
}
}, [])
const validateform = () => {
console.log(document)
if (isNaN((Number)(document))) {
setMessageToast('Documento pode conter apenas números!')
setShowToast(true)
return false
}
if (documentType === 'cpf' && !validateCpf(document)) {
setMessageToast('CPF inválido!')
setShowToast(true)
return false
}
return true
}
const handleUpdateUserDocuments = async () => {
if (!validateform()) {
return
}
usersRoutes.update({ document_type: documentType, document: document }).then(response => {
if (response.status === 'error') {
setMessageToast(response.message);
setShowToast(true);
return
}
console.log(response)
}).catch((err) => {
setMessageToast(err);
setShowToast(true);
})
};
const handleChangeDocumentType = (document_type: string) => {
switch(document_type) {
case 'cpf':
setDocumentType('cpf') // workaround para o problema de setState para valores vindos de um evento sendo triggerado por um ion-select
setDocumentMaxLength(11)
break;
case 'cnh':
setDocumentType('cnh') // workaround para o problema de setState para valores vindos de um evento sendo triggerado por um ion-select
setDocumentMaxLength(11)
break;
case 'rg':
setDocumentType('rg') // workaround para o problema de setState para valores vindos de um evento sendo triggerado por um ion-select
setDocumentMaxLength(9)
break;
}
}
useEffect(() => {
setDocumentTypes([
{
name: 'cpf',
label: 'CPF',
},
{
name: 'rg',
label: 'RG',
},
{
name: 'cnh',
label: 'CNH',
},
])
}, [])
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>Completar cadastro</IonTitle>
<IonButtons slot="start">
<IonBackButton defaultHref="/perfil/completar" />
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">Completar cadastro</IonTitle>
</IonToolbar>
</IonHeader>
<IonGrid>
<IonRow>
<IonCol>
<IonList lines="full" class="ion-no-margin">
<IonItem>
<IonLabel position="floating"> Tipo de documento</IonLabel>
<IonSelect id="document_type" onIonChange={(e: any) => { handleChangeDocumentType(e.detail.value) } }>
{ documentTypes ? documentTypes.map((document, index) => {
return (<IonSelectOption key={index} value={document.name}>{document.label}</IonSelectOption>)
}) : <></> }
</IonSelect>
</IonItem>
<IonItem>
<IonLabel position="floating"> Documento</IonLabel>
<IonInput
type="text"
value={document}
maxlength={documentMaxLength}
onIonChange={(e: any) => { setDocument(e.target.value); setHasChangedSinceInitialState(true) }}
></IonInput>
</IonItem>
</IonList>
</IonCol>
</IonRow>
</IonGrid>
<IonFab vertical="bottom" horizontal="end" slot="fixed">
<IonFabButton disabled={!hasChangedSinceInitialState} onClick={handleUpdateUserDocuments}>
<IonIcon icon={saveOutline} />
</IonFabButton>
</IonFab>
<IonToast
color='danger'
isOpen={showToast}
onDidDismiss={() => setShowToast(false)}
message={messageToast}
duration={2500}
/>
</IonContent>
</IonPage>
);
};
export default CompletarDocumento;

View File

@@ -0,0 +1,137 @@
import {
IonBackButton,
IonButtons,
IonContent,
IonFab,
IonFabButton,
IonHeader,
IonIcon,
IonList,
IonPage,
IonSelect,
IonSelectOption,
IonTitle,
IonToast,
IonToolbar
} from "@ionic/react";
import React, { useEffect, useState } from "react";
import { IonGrid, IonRow, IonCol } from "@ionic/react";
import { useHistory, useLocation } from "react-router-dom";
import {
IonItem,
IonLabel,
IonInput,
} from "@ionic/react";
import { saveOutline } from "ionicons/icons";
import * as usersRoutes from '../../services/api/users';
interface documentTypesInterface {
label: string;
name: string;
}
const CompletarTelefone: React.FC = () => {
const [hasChangedSinceInitialState, setHasChangedSinceInitialState] = useState(false);
const [phone, setPhone] = useState('');
const [showToast, setShowToast] = useState(false);
const [messageToast, setMessageToast] = useState('');
const history = useHistory();
const location = useLocation();
useEffect(() => {
if (!location.state) {
history.push({ pathname: '/perfil/completar' })
}
}, [])
const validateform = () => {
if (isNaN((Number)(phone))) {
setMessageToast('O telefone pode conter apenas números!')
setShowToast(true)
return false
}
return true
}
const handleUpdateUserDocuments = async () => {
if (!validateform()) {
return
}
usersRoutes.update({ phone_number: phone }).then(response => {
if (response.status === 'error') {
setMessageToast(response.message);
setShowToast(true);
return
}
console.log(response)
}).catch((err) => {
setMessageToast(err);
setShowToast(true);
})
};
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>Completar cadastro</IonTitle>
<IonButtons slot="start">
<IonBackButton defaultHref="/perfil/completar" />
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">Completar cadastro</IonTitle>
</IonToolbar>
</IonHeader>
<IonGrid>
<IonRow>
<IonCol>
<IonList lines="full" class="ion-no-margin">
<IonItem>
<IonLabel position="floating"> Telefone</IonLabel>
<IonInput
type="text"
value={phone}
maxlength={11}
onIonChange={(e: any) => { setPhone(e.target.value); setHasChangedSinceInitialState(true) }}
></IonInput>
</IonItem>
</IonList>
</IonCol>
</IonRow>
</IonGrid>
<IonFab vertical="bottom" horizontal="end" slot="fixed">
<IonFabButton disabled={!hasChangedSinceInitialState} onClick={handleUpdateUserDocuments}>
<IonIcon icon={saveOutline} />
</IonFabButton>
</IonFab>
<IonToast
color='danger'
isOpen={showToast}
onDidDismiss={() => setShowToast(false)}
message={messageToast}
duration={2500}
/>
</IonContent>
</IonPage>
);
};
export default CompletarTelefone;

View File

@@ -231,9 +231,10 @@ const CadastroVan: React.FC = () => {
/> />
</IonItem> </IonItem>
{/* TODO, problema de setState para valores vindos de um evento sendo triggerado por um ion-select */}
<IonItem> <IonItem>
<IonLabel>Marca</IonLabel> <IonLabel>Marca</IonLabel>
<IonSelect value={inputValues.carBrand} onIonChange={(e: any) => setInputValues({ carBrand: e.target.value })}> <IonSelect onIonChange={(e: any) => { setInputValues({ carBrand: e.detail.value }) }}>
{ carModels ? carModels.map((carModel, index) => { { carModels ? carModels.map((carModel, index) => {
return (<IonSelectOption key={index} value={carModel.name}>{carModel.name}</IonSelectOption>) return (<IonSelectOption key={index} value={carModel.name}>{carModel.name}</IonSelectOption>)
}) : <></> } }) : <></> }

View File

@@ -38,7 +38,7 @@ const Home: React.FC = () => {
return ( return (
<IonPage> <IonPage>
<IonContent> <IonContent>
<IonButton onClick={() => { history.push({ pathname: '/usuario/56520ae7-faf8-4444-a82b-7f3990ab02d8' }); }}>Ir para o perfil de outra pessoa</IonButton> {/* <IonButton onClick={() => { history.push({ pathname: '/usuario/56520ae7-faf8-4444-a82b-7f3990ab02d8' }); }}>Ir para o perfil de outra pessoa</IonButton> */}
</IonContent> </IonContent>
</IonPage> </IonPage>
); );

View File

@@ -42,27 +42,23 @@ const Perfil: React.FC<ScanNewProps> = (props) => {
const [isVisitor, setIsVisitor] = useState(true) const [isVisitor, setIsVisitor] = useState(true)
const [incompleteProfile, setIncompleteProfile] = useState(false)
const [showToast, setShowToast] = useState(false); const [showToast, setShowToast] = useState(false);
const [messageToast, setMessageToast] = useState(''); const [messageToast, setMessageToast] = useState('');
const [inputValues, setInputValues] = useReducer( const [inputValues, setInputValues] = useReducer(
(state: any, newState: any) => ({ ...state, ...newState }), (state: any, newState: any) => ({ ...state, ...newState }),
{ {
id: '',
name: '', name: '',
lastname: '', lastname: '',
email: '', email: '',
phone_number: '',
birth_date: '', birth_date: '',
bio: '', bio: '',
} document_type: '',
); document: '',
const [inputSocialInformationValues, setInputSocialInformationValues] = useReducer(
(state: any, newState: any) => ({ ...state, ...newState }),
{
phone: '',
whatsapp: '',
facebook: '',
telegram: '',
} }
); );
@@ -122,44 +118,24 @@ const Perfil: React.FC<ScanNewProps> = (props) => {
if (isMounted) { if (isMounted) {
setInputValues({ setInputValues({
'id': userId,
'name': userData.name, 'name': userData.name,
'lastname': userData.lastname, 'lastname': userData.lastname,
'email': userData.email, 'email': userData.email,
'phone_number': userData.phone_number,
'birth_date': userData.birth_date, 'birth_date': userData.birth_date,
'bio': userData.bio 'bio': userData.bio,
'document_type': userData.document_type,
'document': userData.document
}); });
if (!props.match.params.id) { if (!props.match.params.id) {
setIsVisitor(false) setIsVisitor(false)
} }
}
}
// get user social info
const getUserSocialInfoRes = await usersService.getUserSocialInfo(userId)
if (getUserSocialInfoRes.error) { if (!userData.document || !userData.phone_number) {
if (isVisitor && props.match.params.id) { setIncompleteProfile(true)
setMessageToast('Usuário não existe!') }
setShowToast(true)
history.push({ pathname: '/home' })
} else {
setMessageToast(getUserSocialInfoRes.error.errorMessage)
setShowToast(true)
}
return
}
if (getUserSocialInfoRes.data) {
const userSocialData = getUserSocialInfoRes.data
if (isMounted) {
setInputSocialInformationValues({
'phone': userSocialData.phone,
'whatsapp': userSocialData.whatsapp,
'facebook': userSocialData.facebook,
'telegram': userSocialData.telegram,
});
} }
} }
} }
@@ -226,42 +202,15 @@ const Perfil: React.FC<ScanNewProps> = (props) => {
<IonCardTitle>Informações de contato</IonCardTitle> <IonCardTitle>Informações de contato</IonCardTitle>
</IonCardHeader> </IonCardHeader>
<IonCardContent> <IonCardContent>
{ !inputSocialInformationValues.phone && !inputSocialInformationValues.whatsapp && !inputSocialInformationValues.facebook && !inputSocialInformationValues.telegram ? { !inputValues.phone_number ?
<>Sem informações de contato.</> <>Sem informações de contato.</>
: <> : <>
{ {
inputSocialInformationValues.phone ? inputValues.phone_number ?
<> <>
<IonChip> <IonChip>
<IonIcon icon={callOutline} /> <IonIcon icon={callOutline} />
<IonLabel>{inputSocialInformationValues.phone}</IonLabel> <IonLabel>{inputValues.phone_number}</IonLabel>
</IonChip>
</> : <></>
}
{ inputSocialInformationValues.whatsapp ?
<>
<IonChip>
<IonIcon icon={logoWhatsapp} />
<IonLabel>{inputSocialInformationValues.whatsapp}</IonLabel>
</IonChip>
</> : <></>
}
{ inputSocialInformationValues.facebook ?
<>
<IonChip>
<IonIcon icon={logoFacebook} />
<IonLabel>{inputSocialInformationValues.facebook}</IonLabel>
</IonChip>
</> : <></>
}
{ inputSocialInformationValues.telegram ?
<>
<IonChip>
<IonIcon icon={callOutline} />
<IonLabel>{inputSocialInformationValues.telegram}</IonLabel>
</IonChip> </IonChip>
</> : <></> </> : <></>
} }
@@ -277,10 +226,16 @@ const Perfil: React.FC<ScanNewProps> = (props) => {
<IonIcon icon={createOutline} slot="start" /> <IonIcon icon={createOutline} slot="start" />
<IonLabel>Editar perfil</IonLabel> <IonLabel>Editar perfil</IonLabel>
</IonItem> </IonItem>
<IonItem button onClick={() => history.push({ pathname: '/perfil/completar', state: { userData: inputValues } })}>
<IonIcon icon={shieldCheckmarkOutline} slot="start" /> { incompleteProfile ?
<IonLabel>Completar cadastro</IonLabel> <>
</IonItem> <IonItem button onClick={() => history.push({ pathname: '/perfil/completar', state: { userData: inputValues } })}>
<IonIcon icon={shieldCheckmarkOutline} slot="start" />
<IonLabel>Completar cadastro</IonLabel>
</IonItem>
</>
: <></> }
<IonItem button onClick={() => history.push({ pathname: '/cadastro-van'})}> <IonItem button onClick={() => history.push({ pathname: '/cadastro-van'})}>
<IonIcon icon={carOutline} slot="start" /> <IonIcon icon={carOutline} slot="start" />
<IonLabel>Cadastrar Van</IonLabel> <IonLabel>Cadastrar Van</IonLabel>

View File

@@ -68,6 +68,10 @@ const PerfilEditar: React.FC = () => {
); );
useEffect(() => { useEffect(() => {
if (!location.state) {
history.push({ pathname: '/perfil' })
}
let userData = location.state.userData let userData = location.state.userData
setUserData(location.state.userData) setUserData(location.state.userData)

View File

@@ -40,8 +40,9 @@ export interface UpdateUserRequest {
name?: string; name?: string;
email?: string; email?: string;
bio?: string; bio?: string;
cpf?: string; document_type?: string;
cnpj?: string; document?: string;
phone_number?: string;
} }
// export async function get(cpf) { // export async function get(cpf) {

View File

@@ -5,10 +5,11 @@ interface getByIdReturn {
name: string; name: string;
lastname: string; lastname: string;
email: string; email: string;
phone_number: string;
birth_date: string; birth_date: string;
bio: string; bio: string;
cpf: string; document_type: string;
cnpj: string; document: string;
}, },
error?: { error?: {
errorMessage: string; errorMessage: string;
@@ -23,10 +24,11 @@ interface getByIdRes {
name: string; name: string;
lastname: string; lastname: string;
email: string; email: string;
phone_number: string;
birth_date: string; birth_date: string;
bio: string; bio: string;
cpf: string; document_type: string;
cnpj: string; document: string;
}, },
} }