Merge branch 'master' of https://github.com/CloudAlb/tcc-vamos-frontend into develop
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -26,3 +26,6 @@ yarn-error.log*
|
|||||||
|
|
||||||
# Optional eslint cache
|
# Optional eslint cache
|
||||||
.eslintcache
|
.eslintcache
|
||||||
|
|
||||||
|
# não commitar url de environment
|
||||||
|
src/constants/environment.ts
|
||||||
|
|||||||
12
mock/db.json
Normal file
12
mock/db.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"cadastro-van": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"placa": "DBE2356",
|
||||||
|
"marca": "Fiat",
|
||||||
|
"modelo": "Ducatto",
|
||||||
|
"numPassageiros": 14,
|
||||||
|
"alugado": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
981
package-lock.json
generated
981
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
13
package.json
13
package.json
@@ -8,6 +8,8 @@
|
|||||||
"@capacitor/haptics": "1.1.4",
|
"@capacitor/haptics": "1.1.4",
|
||||||
"@capacitor/keyboard": "1.2.2",
|
"@capacitor/keyboard": "1.2.2",
|
||||||
"@capacitor/status-bar": "1.0.8",
|
"@capacitor/status-bar": "1.0.8",
|
||||||
|
"@hookform/error-message": "^2.0.0",
|
||||||
|
"@ionic-selectable/core": "^5.0.0-alpha.13",
|
||||||
"@ionic/react": "^6.0.0",
|
"@ionic/react": "^6.0.0",
|
||||||
"@ionic/react-router": "^6.0.0",
|
"@ionic/react-router": "^6.0.0",
|
||||||
"@testing-library/jest-dom": "^5.11.9",
|
"@testing-library/jest-dom": "^5.11.9",
|
||||||
@@ -21,10 +23,16 @@
|
|||||||
"@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",
|
||||||
|
"pigeon-maps": "^0.21.0",
|
||||||
|
"pullstate": "^1.24.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-google-places-autocomplete": "^3.3.4",
|
||||||
|
"react-hook-form": "^7.30.0",
|
||||||
"react-router": "^5.2.0",
|
"react-router": "^5.2.0",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
|
"reselect": "^4.1.5",
|
||||||
"typescript": "^4.1.3",
|
"typescript": "^4.1.3",
|
||||||
"web-vitals": "^0.2.4",
|
"web-vitals": "^0.2.4",
|
||||||
"workbox-background-sync": "^5.1.4",
|
"workbox-background-sync": "^5.1.4",
|
||||||
@@ -38,10 +46,12 @@
|
|||||||
"workbox-range-requests": "^5.1.4",
|
"workbox-range-requests": "^5.1.4",
|
||||||
"workbox-routing": "^5.1.4",
|
"workbox-routing": "^5.1.4",
|
||||||
"workbox-strategies": "^5.1.4",
|
"workbox-strategies": "^5.1.4",
|
||||||
"workbox-streams": "^5.1.4"
|
"workbox-streams": "^5.1.4",
|
||||||
|
"yup": "^0.32.11"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-scripts start",
|
"start": "react-scripts start",
|
||||||
|
"start:mock": "json-server --port 8080 --watch ./mock/db.json",
|
||||||
"build": "react-scripts build",
|
"build": "react-scripts build",
|
||||||
"test": "react-scripts test --transformIgnorePatterns 'node_modules/(?!(@ionic/react|@ionic/react-router|@ionic/core|@stencil/core|ionicons)/)'",
|
"test": "react-scripts test --transformIgnorePatterns 'node_modules/(?!(@ionic/react|@ionic/react-router|@ionic/core|@stencil/core|ionicons)/)'",
|
||||||
"eject": "react-scripts eject"
|
"eject": "react-scripts eject"
|
||||||
@@ -67,6 +77,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"
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="pt-br">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<title>Ionic App</title>
|
<title>Vanmos App</title>
|
||||||
|
|
||||||
<base href="/" />
|
<base href="/" />
|
||||||
|
|
||||||
|
|||||||
108
src/App.tsx
108
src/App.tsx
@@ -1,15 +1,29 @@
|
|||||||
import { Redirect, Route } from 'react-router-dom';
|
import { Redirect, Route } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
IonApp,
|
IonApp,
|
||||||
|
IonIcon,
|
||||||
|
IonLabel,
|
||||||
IonRouterOutlet,
|
IonRouterOutlet,
|
||||||
|
IonTabBar,
|
||||||
|
IonTabButton,
|
||||||
|
IonTabs,
|
||||||
setupIonicReact
|
setupIonicReact
|
||||||
} from '@ionic/react';
|
} from '@ionic/react';
|
||||||
import { IonReactRouter } from '@ionic/react-router';
|
import { IonReactRouter } from '@ionic/react-router';
|
||||||
import Cadastro from './pages/Cadastro';
|
|
||||||
import MainPages from './pages/MainPages';
|
|
||||||
|
|
||||||
// importação das páginas
|
// importação das páginas
|
||||||
import Login from './pages/login/Login';
|
import Cadastro from './pages/Cadastro/Cadastro';
|
||||||
|
import Login from './pages/Login';
|
||||||
|
import Home from './pages/Home';
|
||||||
|
import Perfil from './pages/Perfil';
|
||||||
|
import PerfilEditar from './pages/PerfilEditar';
|
||||||
|
import CadastroVan from './pages/CadastroVan';
|
||||||
|
import CadastroCompletar from './pages/CadastroCompletar/CadastroCompletar';
|
||||||
|
import CompletarDocumento from './pages/CadastroCompletar/CompletarDocumento';
|
||||||
|
import CompletarTelefone from './pages/CadastroCompletar/CompletarTelefone';
|
||||||
|
import Transportes from './pages/Transportes/Transportes';
|
||||||
|
import BuscarPassageiro from './pages/BuscarPassageiro/BuscarPassageiro';
|
||||||
|
import BuscarTransporte from './pages/BuscarTransporte/BuscarTransporte';
|
||||||
|
|
||||||
/* 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';
|
||||||
@@ -30,23 +44,91 @@ import '@ionic/react/css/display.css';
|
|||||||
/* Theme variables */
|
/* Theme variables */
|
||||||
import './theme/variables.css';
|
import './theme/variables.css';
|
||||||
|
|
||||||
|
// import Tabs from './components/Tabs';
|
||||||
|
import { search, home, person } from 'ionicons/icons';
|
||||||
|
import { useState, useContext } from 'react';
|
||||||
|
import React from 'react';
|
||||||
|
import MinhasVans from './pages/MinhasVans';
|
||||||
|
|
||||||
setupIonicReact();
|
setupIonicReact();
|
||||||
|
|
||||||
const App: React.FC = () => (
|
const routes = (
|
||||||
<IonApp>
|
<>
|
||||||
<IonReactRouter>
|
|
||||||
|
|
||||||
<IonRouterOutlet>
|
|
||||||
<Route exact path="/mainpages" component={MainPages}></Route>
|
|
||||||
<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="/">
|
|
||||||
<Redirect to="/cadastro" />
|
|
||||||
</Route>
|
|
||||||
</IonRouterOutlet>
|
|
||||||
|
|
||||||
|
<Route exact path="/home" component={Home}></Route>
|
||||||
|
|
||||||
|
<Route exact path="/perfil" component={Perfil}></Route>
|
||||||
|
<Route exact path="/perfil/editar" component={PerfilEditar}></Route>
|
||||||
|
<Route exact path="/perfil/completar" component={CadastroCompletar}></Route>
|
||||||
|
<Route exact path="/perfil/completar/documento" component={CompletarDocumento}></Route>
|
||||||
|
<Route exact path="/perfil/completar/telefone" component={CompletarTelefone}></Route>
|
||||||
|
|
||||||
|
<Route exact path="/transportes" component={Transportes}></Route>
|
||||||
|
<Route exact path="/buscar-passageiro" component={BuscarPassageiro}></Route>
|
||||||
|
<Route exact path="/buscar-transporte" component={BuscarTransporte}></Route>
|
||||||
|
|
||||||
|
<Route exact path="/usuario/:id" component={Perfil}></Route>
|
||||||
|
|
||||||
|
<Route exact path="/cadastro-van" component={CadastroVan}></Route>
|
||||||
|
<Route exact path="/minhas-vans" component={MinhasVans}></Route>
|
||||||
|
<Route exact path="/">
|
||||||
|
<Redirect to="/login" />
|
||||||
|
</Route>
|
||||||
|
</>)
|
||||||
|
|
||||||
|
interface IUserManager {
|
||||||
|
setIsLoggedIn: Function;
|
||||||
|
}
|
||||||
|
|
||||||
|
const user: IUserManager = {
|
||||||
|
setIsLoggedIn: () => {}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const UserContext = React.createContext<IUserManager>(user);
|
||||||
|
|
||||||
|
const IonicApp: React.FC = () => {
|
||||||
|
|
||||||
|
const [isLoggedIn, setIsLoggedIn] = useState(false);
|
||||||
|
const user = useContext(UserContext);
|
||||||
|
|
||||||
|
user.setIsLoggedIn = setIsLoggedIn;
|
||||||
|
|
||||||
|
return(
|
||||||
|
<IonApp>
|
||||||
|
<IonReactRouter>
|
||||||
|
{isLoggedIn ? (
|
||||||
|
<IonTabs>
|
||||||
|
<IonRouterOutlet>{routes}</IonRouterOutlet>
|
||||||
|
|
||||||
|
<IonTabBar slot="bottom">
|
||||||
|
<IonTabButton tab="buscar" href="/buscar">
|
||||||
|
<IonIcon icon={search} />
|
||||||
|
<IonLabel>Buscar</IonLabel>
|
||||||
|
</IonTabButton>
|
||||||
|
<IonTabButton tab="home" href="/home">
|
||||||
|
<IonIcon icon={home} />
|
||||||
|
<IonLabel>Home</IonLabel>
|
||||||
|
</IonTabButton>
|
||||||
|
<IonTabButton tab="perfil" href="/perfil">
|
||||||
|
<IonIcon icon={person} />
|
||||||
|
<IonLabel>Perfil</IonLabel>
|
||||||
|
</IonTabButton>
|
||||||
|
</IonTabBar>
|
||||||
|
</IonTabs>
|
||||||
|
) : (<IonRouterOutlet>{routes}</IonRouterOutlet>)}
|
||||||
</IonReactRouter>
|
</IonReactRouter>
|
||||||
</IonApp>
|
</IonApp>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const App: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<UserContext.Provider value={user}>
|
||||||
|
<IonicApp />
|
||||||
|
</UserContext.Provider>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
|||||||
24
src/LocalStorage.ts
Normal file
24
src/LocalStorage.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
const tokenId = 'token';
|
||||||
|
const productDetails = '@productDetails';
|
||||||
|
|
||||||
|
const LocalStorage = {
|
||||||
|
getToken: (): string => {
|
||||||
|
const tokenId = localStorage.getItem('tokenId')
|
||||||
|
|
||||||
|
if (!tokenId) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokenId
|
||||||
|
},
|
||||||
|
|
||||||
|
setToken: (token: string) => {
|
||||||
|
localStorage.setItem('tokenId', token)
|
||||||
|
},
|
||||||
|
|
||||||
|
clearToken: () => {
|
||||||
|
localStorage.removeItem('tokenId')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LocalStorage
|
||||||
@@ -10,10 +10,8 @@ export const Action = (props: ComponentProps) => (
|
|||||||
|
|
||||||
<IonRow className="ion-text-center ion-justify-content-center">
|
<IonRow className="ion-text-center ion-justify-content-center">
|
||||||
<IonCol size="12">
|
<IonCol size="12">
|
||||||
<p>
|
|
||||||
{ props.message }
|
{ props.message }
|
||||||
<IonRouterLink className="custom-link" routerLink={ props.link }> { props.text } →</IonRouterLink>
|
<IonRouterLink className="custom-link" routerLink={ props.link }> { props.text } →</IonRouterLink>
|
||||||
</p>
|
|
||||||
</IonCol>
|
</IonCol>
|
||||||
</IonRow>
|
</IonRow>
|
||||||
);
|
);
|
||||||
45
src/components/Tabs.tsx
Normal file
45
src/components/Tabs.tsx
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { IonTabs, IonTabBar, IonTabButton, IonIcon, IonLabel } from '@ionic/react';
|
||||||
|
|
||||||
|
import { logInOutline, logInSharp, personOutline, personSharp } from 'ionicons/icons';
|
||||||
|
|
||||||
|
interface AppTab {
|
||||||
|
label: string;
|
||||||
|
iosIcon: string;
|
||||||
|
mdIcon: string;
|
||||||
|
// badge: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const appPages: AppTab[] = [
|
||||||
|
{
|
||||||
|
label: 'Login',
|
||||||
|
iosIcon: logInOutline,
|
||||||
|
mdIcon: logInSharp,
|
||||||
|
// badge: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Cadastro',
|
||||||
|
iosIcon: personOutline,
|
||||||
|
mdIcon: personSharp,
|
||||||
|
// badge: '',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const Tabs: React.FC = () => {
|
||||||
|
return (
|
||||||
|
|
||||||
|
<IonTabs>
|
||||||
|
{appPages.map((appPage, index) => {
|
||||||
|
<IonTabBar key={index} slot="bottom">
|
||||||
|
<IonTabButton tab="speakers">
|
||||||
|
<IonIcon ios={appPage.iosIcon} md={appPage.mdIcon} />
|
||||||
|
<IonLabel>{appPage.label}</IonLabel>
|
||||||
|
</IonTabButton>
|
||||||
|
</IonTabBar>
|
||||||
|
|
||||||
|
})}
|
||||||
|
</IonTabs>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Tabs
|
||||||
60
src/components/UserSearchInfos/UserSearchInfos.css
Normal file
60
src/components/UserSearchInfos/UserSearchInfos.css
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
.overlayContainer {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
/* // align-items: center;
|
||||||
|
// align-content: center; */
|
||||||
|
|
||||||
|
padding: 1rem;
|
||||||
|
|
||||||
|
width: 12rem;
|
||||||
|
height: fit-content;
|
||||||
|
background-color: white;
|
||||||
|
|
||||||
|
border-radius: 5px;
|
||||||
|
|
||||||
|
box-shadow: rgba(149, 157, 165, 0.2) 0px 8px 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlayContainer ion-card-subtitle {
|
||||||
|
|
||||||
|
font-size: 0.7rem;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlayContainer ion-note {
|
||||||
|
|
||||||
|
font-size: 0.6rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlayContainer ion-badge {
|
||||||
|
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlayContainer p {
|
||||||
|
color: black;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
margin-bottom: 0.3rem;
|
||||||
|
font-size: 0.6rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
align-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlayContainer:after {
|
||||||
|
|
||||||
|
content:'';
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 70%;
|
||||||
|
margin-left: -50px;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-top: solid 10px white;
|
||||||
|
border-left: solid 10px transparent;
|
||||||
|
border-right: solid 10px transparent;
|
||||||
|
}
|
||||||
65
src/components/UserSearchInfos/UserSearchInfos.tsx
Normal file
65
src/components/UserSearchInfos/UserSearchInfos.tsx
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import {
|
||||||
|
IonBadge,
|
||||||
|
IonButton,
|
||||||
|
IonCardSubtitle,
|
||||||
|
IonCol,
|
||||||
|
IonIcon,
|
||||||
|
IonNote,
|
||||||
|
IonRow,
|
||||||
|
} from "@ionic/react";
|
||||||
|
import {
|
||||||
|
arrowForward,
|
||||||
|
call,
|
||||||
|
callOutline,
|
||||||
|
navigateOutline,
|
||||||
|
} from "ionicons/icons";
|
||||||
|
import "./UserSearchInfos.css";
|
||||||
|
|
||||||
|
export const UserSearchInfos = (record: any) => {
|
||||||
|
console.log(record.record);
|
||||||
|
return (
|
||||||
|
<div className="overlayContainer">
|
||||||
|
<IonCardSubtitle>{record.record.user.name}</IonCardSubtitle>
|
||||||
|
{/* <IonNote color="medium">{ record.record.addressFrom }</IonNote> */}
|
||||||
|
<IonBadge color="dark">
|
||||||
|
{record.record.user.star_rating
|
||||||
|
? `${record.record.user.star_rating} estrelas`
|
||||||
|
: "Sem avaliações"}
|
||||||
|
</IonBadge>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<IonIcon icon={navigateOutline} size='large' />
|
||||||
|
{record.record.address_to}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{record.record.user.phone_number && (
|
||||||
|
<p>
|
||||||
|
<IonIcon icon={call} />
|
||||||
|
{record.record.user.phone_number}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<IonRow className="ion-no-padding ion-no-margin ion-margin-top">
|
||||||
|
<IonCol size="12" className="ion-no-padding ion-no-margin">
|
||||||
|
<IonButton
|
||||||
|
color="primary"
|
||||||
|
fill="solid"
|
||||||
|
size="small"
|
||||||
|
expand="block"
|
||||||
|
routerLink={`/perfil/${record.record.user.id_user}`}
|
||||||
|
>
|
||||||
|
Ver mais informações →
|
||||||
|
</IonButton>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
|
||||||
|
<IonRow className="ion-no-padding ion-no-margin">
|
||||||
|
<IonCol size="12" className="ion-no-padding ion-no-margin">
|
||||||
|
<IonButton color="primary" fill="outline" size="small" expand="block">
|
||||||
|
<IonIcon icon={callOutline} />
|
||||||
|
</IonButton>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import environment from "../environments/environment";
|
import environment from "../environments/environment";
|
||||||
|
|
||||||
function getBaseUrl() {
|
const getBaseUrl = (): string => {
|
||||||
const { hostname } = window.location;
|
// const { hostname } = window.location;
|
||||||
const { url } = environment;
|
const { url } = environment;
|
||||||
|
|
||||||
let apiUrl = null;
|
let apiUrl = null;
|
||||||
|
|||||||
8
src/constants/routes/carsRoutes.ts
Normal file
8
src/constants/routes/carsRoutes.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
const carsRoutesDefault = '/cars';
|
||||||
|
const carsRoutes = {
|
||||||
|
list: {
|
||||||
|
url: `${carsRoutesDefault}/list`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default carsRoutes;
|
||||||
11
src/constants/routes/sessionRoutes.ts
Normal file
11
src/constants/routes/sessionRoutes.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
const sessionRoutesDefault = '/sessions';
|
||||||
|
const sessionRoutes = {
|
||||||
|
create: {
|
||||||
|
url: `${sessionRoutesDefault}/`
|
||||||
|
},
|
||||||
|
refresh: {
|
||||||
|
url: `${sessionRoutesDefault}/refresh`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default sessionRoutes;
|
||||||
27
src/constants/routes/usersRoutes.ts
Normal file
27
src/constants/routes/usersRoutes.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
const usersRoutesDefault = '/users';
|
||||||
|
const usersRoutes = {
|
||||||
|
create: {
|
||||||
|
url: `${usersRoutesDefault}`
|
||||||
|
},
|
||||||
|
get: {
|
||||||
|
url: `${usersRoutesDefault}`
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
url: `${usersRoutesDefault}/edit`
|
||||||
|
},
|
||||||
|
checkIfUserIsDriver: {
|
||||||
|
url: `${usersRoutesDefault}/isDriver`
|
||||||
|
},
|
||||||
|
getSocialInfo: {
|
||||||
|
url: `${usersRoutesDefault}/social`
|
||||||
|
},
|
||||||
|
// TODO, depois corrigir
|
||||||
|
getUsersSearching: {
|
||||||
|
url: `/search/inraio`
|
||||||
|
},
|
||||||
|
createUserSearch: {
|
||||||
|
url: `${usersRoutesDefault}/search`
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default usersRoutes;
|
||||||
17
src/constants/routes/vansLocatorsRoutes.ts
Normal file
17
src/constants/routes/vansLocatorsRoutes.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
const vansRoutesDefault = '/vans/locator';
|
||||||
|
const vansRoutes = {
|
||||||
|
list: {
|
||||||
|
url: `${vansRoutesDefault}/list`
|
||||||
|
},
|
||||||
|
getById: {
|
||||||
|
url: `${vansRoutesDefault}/`
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
url: `${vansRoutesDefault}/`
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
url: `${vansRoutesDefault}/edit`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default vansRoutes;
|
||||||
20
src/constants/routes/vansRoutes.ts
Normal file
20
src/constants/routes/vansRoutes.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
const vansRoutesDefault = '/vans';
|
||||||
|
const vansRoutes = {
|
||||||
|
list: {
|
||||||
|
url: `${vansRoutesDefault}/list`
|
||||||
|
},
|
||||||
|
getByPlate: {
|
||||||
|
url: `${vansRoutesDefault}/plate`
|
||||||
|
},
|
||||||
|
getByUserId: {
|
||||||
|
url: `${vansRoutesDefault}/user`
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
url: `${vansRoutesDefault}/`
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
url: `${vansRoutesDefault}/`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default vansRoutes;
|
||||||
8
src/models/van.model.ts
Normal file
8
src/models/van.model.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export type Van = {
|
||||||
|
id: number;
|
||||||
|
carPlate: string;
|
||||||
|
carBrand: string;
|
||||||
|
carModel: string;
|
||||||
|
maxPassengers: number;
|
||||||
|
isRent: string;
|
||||||
|
};
|
||||||
0
src/pages/BuscarPassageiro/BuscarPassageiro.css
Normal file
0
src/pages/BuscarPassageiro/BuscarPassageiro.css
Normal file
127
src/pages/BuscarPassageiro/BuscarPassageiro.tsx
Normal file
127
src/pages/BuscarPassageiro/BuscarPassageiro.tsx
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
import { IonContent, IonPage, IonFab, IonFabButton, IonIcon } from '@ionic/react';
|
||||||
|
import { search } from 'ionicons/icons';
|
||||||
|
import './BuscarPassageiro.css';
|
||||||
|
|
||||||
|
import { Map, Marker, Overlay } from "pigeon-maps";
|
||||||
|
import { maptiler } from 'pigeon-maps/providers';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import RecordsStore from '../../store/RecordsStore';
|
||||||
|
import { fetchRecords } from '../../store/Selectors';
|
||||||
|
import { getUsersSearching } from '../../services/api/users';
|
||||||
|
import { UserSearchInfos } from '../../components/UserSearchInfos/UserSearchInfos';
|
||||||
|
|
||||||
|
const maptilerProvider = maptiler('d5JQJPLLuap8TkJJlTdJ', 'streets');
|
||||||
|
|
||||||
|
const BuscarPassageiro: React.FC = () => {
|
||||||
|
|
||||||
|
// UNCOMMENT THESE TO USE CURRENT LOCATION.
|
||||||
|
|
||||||
|
// const [ currentPoint, setCurrentPoint ] = useState(false);
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
|
||||||
|
// const getCurrentLocation = async () => {
|
||||||
|
|
||||||
|
// const fetchedLocation = await getLocation();
|
||||||
|
// setCurrentPoint(fetchedLocation.currentLocation);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// getCurrentLocation();
|
||||||
|
// }, []);
|
||||||
|
|
||||||
|
// useIonViewWillEnter(() => {
|
||||||
|
|
||||||
|
// getUsersSearching(currentPoint);
|
||||||
|
// });
|
||||||
|
|
||||||
|
const [ currentPoint, setCurrentPoint ] = useState({ latitude: -22.907829, longitude: -47.062943 });
|
||||||
|
|
||||||
|
const records = RecordsStore.useState(fetchRecords);
|
||||||
|
const center = { latitude: -22.907829, longitude: -47.062943 };
|
||||||
|
|
||||||
|
const [ results, setResults ] = useState([]);
|
||||||
|
const [ zoom, setZoom ] = useState(14);
|
||||||
|
|
||||||
|
const [ moveMode, setMoveMode ] = useState(false);
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
|
||||||
|
// const getData = async () => {
|
||||||
|
|
||||||
|
// await getUsersSearching(currentPoint);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// getData();
|
||||||
|
// }, [ currentPoint ]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
|
||||||
|
setResults(records);
|
||||||
|
}, [ records ]);
|
||||||
|
|
||||||
|
const hideMarkers = () => {
|
||||||
|
console.log('entrou')
|
||||||
|
const tempRecords = JSON.parse(JSON.stringify(results));
|
||||||
|
tempRecords.forEach((tempRecord:any) => tempRecord.showInfo = false);
|
||||||
|
console.log(tempRecords)
|
||||||
|
setResults(tempRecords);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleMap = (e:any) => {
|
||||||
|
setCurrentPoint({ latitude: e.center[0], longitude: e.center[1] });
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchResults = async () => {
|
||||||
|
await getUsersSearching(currentPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
const showMarkerInfo = (e:any, index:any) => {
|
||||||
|
|
||||||
|
const tempRecords = JSON.parse(JSON.stringify(results));
|
||||||
|
|
||||||
|
// Hide all current marker infos
|
||||||
|
!tempRecords[index].showInfo && tempRecords.forEach((tempRecord:any) => tempRecord.showInfo = false);
|
||||||
|
tempRecords[index].showInfo = !tempRecords[index].showInfo;
|
||||||
|
|
||||||
|
console.log(tempRecords)
|
||||||
|
setResults(tempRecords);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IonPage>
|
||||||
|
<IonContent fullscreen>
|
||||||
|
{/* { results &&
|
||||||
|
<> */}
|
||||||
|
<Map onBoundsChanged={e => handleMap(e)} defaultCenter={ [center.latitude, center.longitude] } defaultZoom={ zoom } provider={ maptilerProvider } touchEvents={ true }>
|
||||||
|
|
||||||
|
{results && results.map((record:{latitude_from:any, longitude_from:any}, index) => {
|
||||||
|
return <Marker onClick={ e => showMarkerInfo(e, index) } key={ index } color="#3578e5" width={ 50 } anchor={ [ parseFloat(record.latitude_from), parseFloat(record.longitude_from) ] } />
|
||||||
|
})}
|
||||||
|
|
||||||
|
{ results.map((record:any, index) => {
|
||||||
|
|
||||||
|
if (record.showInfo) {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Overlay key={ index } anchor={ [ parseFloat(record.latitude_from), parseFloat(record.longitude_from) ] } offset={[95, 304]}>
|
||||||
|
<UserSearchInfos record={ record } />
|
||||||
|
</Overlay>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</Map>
|
||||||
|
|
||||||
|
<IonFab vertical="bottom" horizontal="end" slot="fixed">
|
||||||
|
<IonFabButton>
|
||||||
|
<IonIcon onClick={searchResults} icon={search} />
|
||||||
|
</IonFabButton>
|
||||||
|
</IonFab>
|
||||||
|
{/* </> */}
|
||||||
|
{/* } */}
|
||||||
|
</IonContent>
|
||||||
|
</IonPage>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BuscarPassageiro;
|
||||||
57
src/pages/BuscarTransporte/BuscarTransporte.css
Normal file
57
src/pages/BuscarTransporte/BuscarTransporte.css
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
.inputs-from-to{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-search{
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.latest-searches{
|
||||||
|
margin: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.div_from_to{
|
||||||
|
max-width: 70%;
|
||||||
|
margin-inline: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-align-vcenter{
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-forward{
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-search-modal{
|
||||||
|
background-color: var(--ion-toolbar-background);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 1rem;
|
||||||
|
margin: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-return-modal{
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-search-results{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin: 1rem;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
border-bottom: 1px solid var(--ion-toolbar-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-results-modal{
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-autocomplete{
|
||||||
|
margin: 0.5rem 0 0.5rem 0.5rem;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
242
src/pages/BuscarTransporte/BuscarTransporte.tsx
Normal file
242
src/pages/BuscarTransporte/BuscarTransporte.tsx
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
import {
|
||||||
|
IonContent,
|
||||||
|
IonPage,
|
||||||
|
IonIcon,
|
||||||
|
IonCard,
|
||||||
|
IonInput,
|
||||||
|
IonRow,
|
||||||
|
IonCardContent,
|
||||||
|
IonButton,
|
||||||
|
IonSearchbar,
|
||||||
|
IonModal,
|
||||||
|
IonProgressBar,
|
||||||
|
} from "@ionic/react";
|
||||||
|
import {
|
||||||
|
arrowBack,
|
||||||
|
arrowForwardOutline,
|
||||||
|
chevronForwardOutline,
|
||||||
|
locateOutline,
|
||||||
|
locationOutline,
|
||||||
|
timeOutline,
|
||||||
|
} from "ionicons/icons";
|
||||||
|
import "./BuscarTransporte.css";
|
||||||
|
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { autoCompleteAddress } from "../../services/utils";
|
||||||
|
import { useHistory } from "react-router";
|
||||||
|
|
||||||
|
import GooglePlacesAutocomplete, {
|
||||||
|
geocodeByAddress,
|
||||||
|
getLatLng,
|
||||||
|
} from "react-google-places-autocomplete";
|
||||||
|
|
||||||
|
const BuscarTransporte: React.FC = () => {
|
||||||
|
const history = useHistory();
|
||||||
|
const [addressFrom, setAddressFrom] = useState<any>("");
|
||||||
|
const [coordinatesFrom, setCoordinatesFrom] = useState<any>("");
|
||||||
|
const [addressTo, setAddressTo] = useState<any>("");
|
||||||
|
const [coordinatesTo, setCoordinatesTo] = useState<any>("");
|
||||||
|
const [showModalEnd, setShowModalEnd] = useState(false);
|
||||||
|
const [addressResults, setAddressResults] = useState<any>([]);
|
||||||
|
const [inputActive, setInputActive] = useState("");
|
||||||
|
|
||||||
|
// const optionsAddress = async (inputValue: any) => {
|
||||||
|
// let results = await autoCompleteAddress(inputValue)
|
||||||
|
// .then((res) => {
|
||||||
|
// return res.map((item: any) => {
|
||||||
|
// return {
|
||||||
|
// value:
|
||||||
|
// item.geometry.coordinates[0] + "," + item.geometry.coordinates[1],
|
||||||
|
// label: item.properties.formatted,
|
||||||
|
// };
|
||||||
|
// });
|
||||||
|
// })
|
||||||
|
// .catch((err) => {
|
||||||
|
// console.log("Erro ao buscar endereço:", err);
|
||||||
|
// });
|
||||||
|
// setAddressResults(results);
|
||||||
|
// };
|
||||||
|
|
||||||
|
// function setInputActiveOpenModal(input: string) {
|
||||||
|
// setInputActive(input);
|
||||||
|
// setShowModalEnd(true);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// function setAddress(div: any) {
|
||||||
|
// if (inputActive === "from") {
|
||||||
|
// setAddressFrom(div.target.attributes[2].value);
|
||||||
|
// setCoordinatesFrom(div.target.attributes[1].value);
|
||||||
|
// } else {
|
||||||
|
// setAddressTo(div.target.attributes[2].value);
|
||||||
|
// setCoordinatesTo(div.target.attributes[1].value);
|
||||||
|
// }
|
||||||
|
// setShowModalEnd(false);
|
||||||
|
// }
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (addressFrom.label && addressFrom.label.length > 0) {
|
||||||
|
geocodeByAddress(addressFrom.label)
|
||||||
|
.then((results) => getLatLng(results[0]))
|
||||||
|
.then(({ lat, lng }) => setCoordinatesFrom({ lat, lng }));
|
||||||
|
}
|
||||||
|
}, [addressFrom]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (addressTo.label && addressTo.label.length > 0) {
|
||||||
|
geocodeByAddress(addressTo.label)
|
||||||
|
.then((results) => getLatLng(results[0]))
|
||||||
|
.then(({ lat, lng }) => setCoordinatesTo({ lat, lng }));
|
||||||
|
}
|
||||||
|
}, [addressTo]);
|
||||||
|
|
||||||
|
function buscaTransporte(){
|
||||||
|
if (coordinatesFrom && coordinatesTo && addressFrom && addressTo) {
|
||||||
|
history.push({
|
||||||
|
pathname: "/transportes",
|
||||||
|
state: {
|
||||||
|
coordinatesFrom,
|
||||||
|
coordinatesTo,
|
||||||
|
addressFrom,
|
||||||
|
addressTo,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IonPage>
|
||||||
|
<IonContent fullscreen>
|
||||||
|
<IonCard>
|
||||||
|
<IonCardContent>
|
||||||
|
<div className="inputs-from-to">
|
||||||
|
<IonIcon icon={locateOutline}></IonIcon>
|
||||||
|
{/* <IonSearchbar
|
||||||
|
showClearButton="never"
|
||||||
|
onClick={() => setInputActiveOpenModal("from")}
|
||||||
|
value={addressFrom}
|
||||||
|
placeholder="R. José Paulino, 1234 - Centro, Campinas - SP, 13013-001"
|
||||||
|
/> */}
|
||||||
|
<GooglePlacesAutocomplete
|
||||||
|
apiKey="AIzaSyAGfCsaNwxwyj4Ajtfy7MTNADE6JwmnZvA"
|
||||||
|
apiOptions={{ language: "pt-br", region: "br" }}
|
||||||
|
selectProps={{
|
||||||
|
value: addressFrom,
|
||||||
|
onChange: setAddressFrom,
|
||||||
|
className: "input-autocomplete",
|
||||||
|
placeholder: "R. José Paulino, 1234",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="inputs-from-to">
|
||||||
|
<IonIcon icon={locationOutline}></IonIcon>
|
||||||
|
{/* <IonSearchbar
|
||||||
|
showClearButton="never"
|
||||||
|
onClick={() => setInputActiveOpenModal("to")}
|
||||||
|
value={addressTo}
|
||||||
|
placeholder="PUC Campinas"
|
||||||
|
/> */}
|
||||||
|
<GooglePlacesAutocomplete
|
||||||
|
apiKey="AIzaSyAGfCsaNwxwyj4Ajtfy7MTNADE6JwmnZvA"
|
||||||
|
apiOptions={{ language: "pt-br", region: "br" }}
|
||||||
|
selectProps={{
|
||||||
|
value: addressTo,
|
||||||
|
onChange: setAddressTo,
|
||||||
|
className: "input-autocomplete",
|
||||||
|
placeholder: "PUC Campinas",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="button-search">
|
||||||
|
<IonButton
|
||||||
|
color="primary"
|
||||||
|
onClick={() => buscaTransporte()}
|
||||||
|
>
|
||||||
|
Buscar
|
||||||
|
</IonButton>
|
||||||
|
</div>
|
||||||
|
</IonCardContent>
|
||||||
|
</IonCard>
|
||||||
|
<IonRow class="latest-searches">
|
||||||
|
<IonIcon
|
||||||
|
className="icon-align-vcenter"
|
||||||
|
size="large"
|
||||||
|
icon={timeOutline}
|
||||||
|
></IonIcon>
|
||||||
|
<div className="div_from_to">
|
||||||
|
<span>Rua Tal Tal, 154, São Paulo - SP</span>
|
||||||
|
<IonIcon icon={arrowForwardOutline}></IonIcon>
|
||||||
|
<span>USP</span>
|
||||||
|
<br />
|
||||||
|
<small>Há 1 hora</small>
|
||||||
|
</div>
|
||||||
|
<IonIcon
|
||||||
|
className="icon-forward icon-align-vcenter"
|
||||||
|
size="large"
|
||||||
|
icon={chevronForwardOutline}
|
||||||
|
></IonIcon>
|
||||||
|
</IonRow>
|
||||||
|
<IonRow class="latest-searches">
|
||||||
|
<IonIcon
|
||||||
|
className="icon-align-vcenter"
|
||||||
|
size="large"
|
||||||
|
icon={timeOutline}
|
||||||
|
/>
|
||||||
|
<div className="div_from_to">
|
||||||
|
<span>Taquaral</span>
|
||||||
|
<IonIcon icon={arrowForwardOutline}></IonIcon>
|
||||||
|
<span>PUC-Campinas</span>
|
||||||
|
<br />
|
||||||
|
<small>Há 2 hora</small>
|
||||||
|
</div>
|
||||||
|
<IonIcon
|
||||||
|
className="icon-forward icon-align-vcenter"
|
||||||
|
size="large"
|
||||||
|
icon={chevronForwardOutline}
|
||||||
|
/>
|
||||||
|
</IonRow>
|
||||||
|
{/* <IonModal isOpen={showModalEnd}>
|
||||||
|
<IonContent>
|
||||||
|
<div className="header-search-modal">
|
||||||
|
<IonIcon
|
||||||
|
className="icon-return-modal"
|
||||||
|
icon={arrowBack}
|
||||||
|
onClick={() => setShowModalEnd(false)}
|
||||||
|
/>
|
||||||
|
<IonInput
|
||||||
|
onIonChange={(e) => optionsAddress(e.detail.value)}
|
||||||
|
placeholder="R. José Paulino, 1234 - Centro, Campinas - SP, 13013-001"
|
||||||
|
className="search-modal"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{addressResults.length > 0 ? (
|
||||||
|
addressResults.map((item: any) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={item.value}
|
||||||
|
className="modal-search-results"
|
||||||
|
data-value={item.value}
|
||||||
|
data-label={item.label}
|
||||||
|
onClick={(e) => setAddress(e)}
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
<IonIcon
|
||||||
|
className="icon-results-modal"
|
||||||
|
icon={chevronForwardOutline}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<IonProgressBar type="indeterminate" />
|
||||||
|
<br />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</IonContent>
|
||||||
|
</IonModal> */}
|
||||||
|
</IonContent>
|
||||||
|
</IonPage>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BuscarTransporte;
|
||||||
@@ -1,17 +1,23 @@
|
|||||||
import { IonToast, IonProgressBar, IonItem, IonLabel, IonInput, IonBackButton, IonButton, IonButtons, IonCardTitle, IonCol, IonContent, IonGrid, IonHeader, IonPage, IonRow, IonToolbar } from '@ionic/react';
|
import { IonToast, IonProgressBar, IonItem, IonLabel, IonInput, IonBackButton, IonButton, IonButtons, IonCardTitle, IonCol, IonContent, IonGrid, IonHeader, IonPage, IonRow, IonToolbar } from '@ionic/react';
|
||||||
import { arrowBack, logoFacebook, mail } from 'ionicons/icons';
|
import { arrowBack, logoFacebook, mail } from 'ionicons/icons';
|
||||||
import { Action } from '../components/Action';
|
import { Action } from '../../components/Action';
|
||||||
import { useEffect, useState } from 'react';
|
import { useContext, useEffect, useState } from 'react';
|
||||||
import { useHistory, useParams } from 'react-router';
|
import { useHistory, useParams } from 'react-router';
|
||||||
import './Cadastro.css';
|
import './Cadastro.css';
|
||||||
import ModalExample from '../components/Email';
|
import ModalExample from '../../components/Email';
|
||||||
import * as UsersService from '../services/users'
|
import * as UsersService from '../../services/api/users'
|
||||||
|
import LocalStorage from '../../LocalStorage';
|
||||||
|
import { UserContext } from '../../App';
|
||||||
|
import { Color } from '@ionic/react/node_modules/@ionic/core';
|
||||||
|
|
||||||
const Cadastro: React.FC = () => {
|
const Cadastro: React.FC = () => {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
|
const user = useContext(UserContext);
|
||||||
|
|
||||||
const [showToast, setShowToast] = useState(false);
|
const [showToast, setShowToast] = useState(false);
|
||||||
const [messageToast, setMessageToast] = useState('');
|
const [messageToast, setMessageToast] = useState('');
|
||||||
|
const [toastColor, setToastColor] = useState<Color>("primary");
|
||||||
|
|
||||||
const [email, setEmail] = useState<string>('');
|
const [email, setEmail] = useState<string>('');
|
||||||
const [password, setPassword] = useState<string>('');
|
const [password, setPassword] = useState<string>('');
|
||||||
@@ -53,7 +59,7 @@ const Cadastro: React.FC = () => {
|
|||||||
clearResult();
|
clearResult();
|
||||||
|
|
||||||
if(!emailValidate()) {
|
if(!emailValidate()) {
|
||||||
lResult.error = 'O EMAIL é inválido!';
|
lResult.error = 'E-mail inválido!';
|
||||||
lResult.success = false;
|
lResult.success = false;
|
||||||
return lResult;
|
return lResult;
|
||||||
} else if(password.length < 7 || password.length > 12) { //TODO: validar de acordo com a documentação
|
} else if(password.length < 7 || password.length > 12) { //TODO: validar de acordo com a documentação
|
||||||
@@ -66,44 +72,56 @@ const Cadastro: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
|
if(name === '' || email === '' || birthDate === '' || password === '' || confirmPassword === '') {
|
||||||
|
setToastColor("warning")
|
||||||
|
setMessageToast('Nenhum campo pode estar vazio!');
|
||||||
|
setShowToast(true);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if(password !== confirmPassword) {
|
||||||
|
setToastColor("warning")
|
||||||
|
setMessageToast('As senhas devem ser iguais!');
|
||||||
|
setShowToast(true);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if(name != '' && email != '' && birthDate != '' && password != '' && confirmPassword != '') {
|
|
||||||
if(password === confirmPassword){
|
|
||||||
const signUpForm = {
|
const signUpForm = {
|
||||||
name: firstName +' '+ lastName,
|
name: firstName,
|
||||||
|
lastname: lastName,
|
||||||
email: email,
|
email: email,
|
||||||
birth_date: birthDate,
|
birth_date: birthDate,
|
||||||
password: password
|
password: password
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = fieldValidate();
|
let result = fieldValidate();
|
||||||
if((await result).success) {
|
if(!(await result).success) {
|
||||||
|
setToastColor("warning")
|
||||||
let retorno = await UsersService.create(signUpForm);
|
|
||||||
console.log(retorno);
|
|
||||||
if(retorno.token) {
|
|
||||||
// let signIn = await Api.signIn(email, passwordField);
|
|
||||||
// if(signIn.token) {
|
|
||||||
// await AsyncStorage.setItem('token', signIn.token);
|
|
||||||
// await AsyncStorage.setItem('cpf', retorno.cpf);
|
|
||||||
history.push('home');
|
|
||||||
|
|
||||||
} else {
|
|
||||||
setMessageToast(retorno.message);
|
|
||||||
setShowToast(true);
|
|
||||||
}
|
|
||||||
} else{
|
|
||||||
setMessageToast(lResult.error);
|
setMessageToast(lResult.error);
|
||||||
setShowToast(true);
|
setShowToast(true);
|
||||||
|
return
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
setMessageToast('As senhas devem ser iguais!');
|
let retorno = await UsersService.create(signUpForm);
|
||||||
|
|
||||||
|
if(!retorno.token) {
|
||||||
|
setToastColor('danger')
|
||||||
|
setMessageToast(retorno.message);
|
||||||
setShowToast(true);
|
setShowToast(true);
|
||||||
|
return
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
setMessageToast('Nenhum campo pode ser nulo!');
|
LocalStorage.setToken(retorno.token.token);
|
||||||
setShowToast(true);
|
|
||||||
|
user.setIsLoggedIn(true);
|
||||||
|
|
||||||
|
history.push({ pathname: '/home', state: {
|
||||||
|
redirectData: {
|
||||||
|
showToastMessage: true,
|
||||||
|
toastColor: "success",
|
||||||
|
toastMessage: "Usuário cadastrado com sucesso!",
|
||||||
}
|
}
|
||||||
|
}})
|
||||||
};
|
};
|
||||||
|
|
||||||
const { name } = useParams<{ name: string; }>();
|
const { name } = useParams<{ name: string; }>();
|
||||||
@@ -113,7 +131,7 @@ const Cadastro: React.FC = () => {
|
|||||||
<IonHeader>
|
<IonHeader>
|
||||||
<IonToolbar>
|
<IonToolbar>
|
||||||
<IonButtons slot="start">
|
<IonButtons slot="start">
|
||||||
<IonBackButton text={''} icon={arrowBack} defaultHref='mainpages' />
|
<IonBackButton text={''} icon={arrowBack} defaultHref='login' />
|
||||||
</IonButtons>
|
</IonButtons>
|
||||||
</IonToolbar>
|
</IonToolbar>
|
||||||
</IonHeader>
|
</IonHeader>
|
||||||
@@ -125,14 +143,6 @@ const Cadastro: React.FC = () => {
|
|||||||
<IonCardTitle>Cadastro</IonCardTitle>
|
<IonCardTitle>Cadastro</IonCardTitle>
|
||||||
</IonCol>
|
</IonCol>
|
||||||
</IonRow>
|
</IonRow>
|
||||||
{/* <IonItem>
|
|
||||||
<IonIcon icon={logoFacebook} />
|
|
||||||
Continuar com Facebook
|
|
||||||
</IonItem>
|
|
||||||
<IonItem>
|
|
||||||
<IonIcon icon={mail} />
|
|
||||||
Continuar com e-mail
|
|
||||||
</IonItem> */}
|
|
||||||
<IonRow>
|
<IonRow>
|
||||||
<IonCol size="12">
|
<IonCol size="12">
|
||||||
<div id='nome-sobrenome'>
|
<div id='nome-sobrenome'>
|
||||||
@@ -151,7 +161,7 @@ const Cadastro: React.FC = () => {
|
|||||||
<IonLabel position='floating'>Sobrenome</IonLabel>
|
<IonLabel position='floating'>Sobrenome</IonLabel>
|
||||||
<IonInput
|
<IonInput
|
||||||
clearInput
|
clearInput
|
||||||
onIonInput={(e: any) => setLastName(e.target.value)}
|
onIonChange={(e: any) => setLastName(e.target.value)}
|
||||||
>
|
>
|
||||||
</IonInput>
|
</IonInput>
|
||||||
</IonItem>
|
</IonItem>
|
||||||
@@ -162,7 +172,7 @@ const Cadastro: React.FC = () => {
|
|||||||
<IonInput
|
<IonInput
|
||||||
clearInput
|
clearInput
|
||||||
type='email'
|
type='email'
|
||||||
onIonInput={(e: any) => setEmail(e.target.value)}
|
onIonChange={(e: any) => setEmail(e.target.value)}
|
||||||
>
|
>
|
||||||
</IonInput>
|
</IonInput>
|
||||||
</IonItem>
|
</IonItem>
|
||||||
@@ -171,7 +181,7 @@ const Cadastro: React.FC = () => {
|
|||||||
<IonLabel position='stacked'>Data de nascimento</IonLabel>
|
<IonLabel position='stacked'>Data de nascimento</IonLabel>
|
||||||
<IonInput
|
<IonInput
|
||||||
type='date'
|
type='date'
|
||||||
onIonInput={(e: any) => setBirthDate(e.target.value)}
|
onIonChange={(e: any) => setBirthDate(e.target.value)}
|
||||||
>
|
>
|
||||||
</IonInput>
|
</IonInput>
|
||||||
</IonItem>
|
</IonItem>
|
||||||
@@ -181,7 +191,7 @@ const Cadastro: React.FC = () => {
|
|||||||
<IonInput
|
<IonInput
|
||||||
clearInput
|
clearInput
|
||||||
type='password'
|
type='password'
|
||||||
onIonInput={(e: any) => setPassword(e.target.value)}
|
onIonChange={(e: any) => setPassword(e.target.value)}
|
||||||
></IonInput>
|
></IonInput>
|
||||||
</IonItem>
|
</IonItem>
|
||||||
<IonItem>
|
<IonItem>
|
||||||
@@ -189,7 +199,7 @@ const Cadastro: React.FC = () => {
|
|||||||
<IonInput
|
<IonInput
|
||||||
clearInput
|
clearInput
|
||||||
type='password'
|
type='password'
|
||||||
onIonInput={(e: any) => setConfirmPassword(e.target.value)}
|
onIonChange={(e: any) => setConfirmPassword(e.target.value)}
|
||||||
></IonInput>
|
></IonInput>
|
||||||
</IonItem>
|
</IonItem>
|
||||||
|
|
||||||
@@ -202,9 +212,9 @@ const Cadastro: React.FC = () => {
|
|||||||
<Action message="Já tem conta?" text="Login" link="/login" />
|
<Action message="Já tem conta?" text="Login" link="/login" />
|
||||||
</IonGrid>
|
</IonGrid>
|
||||||
{/* <IonProgressBar type="indeterminate"></IonProgressBar><br /> */}
|
{/* <IonProgressBar type="indeterminate"></IonProgressBar><br /> */}
|
||||||
|
|
||||||
<IonToast
|
<IonToast
|
||||||
// cssClass={"toast-notification"}
|
color={toastColor}
|
||||||
color='danger'
|
|
||||||
isOpen={showToast}
|
isOpen={showToast}
|
||||||
onDidDismiss={() => setShowToast(false)}
|
onDidDismiss={() => setShowToast(false)}
|
||||||
message={messageToast}
|
message={messageToast}
|
||||||
142
src/pages/CadastroCompletar/CadastroCompletar.tsx
Normal file
142
src/pages/CadastroCompletar/CadastroCompletar.tsx
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
import {
|
||||||
|
IonBackButton,
|
||||||
|
IonButtons,
|
||||||
|
IonCard,
|
||||||
|
IonCardContent,
|
||||||
|
IonContent,
|
||||||
|
IonHeader,
|
||||||
|
IonIcon,
|
||||||
|
IonItem,
|
||||||
|
IonLabel,
|
||||||
|
IonPage,
|
||||||
|
IonTitle,
|
||||||
|
IonToast,
|
||||||
|
IonToolbar
|
||||||
|
} from "@ionic/react";
|
||||||
|
import React, { useEffect, useReducer, useState } from "react";
|
||||||
|
|
||||||
|
import '../Perfil.css'
|
||||||
|
import { useHistory, useLocation } from "react-router";
|
||||||
|
import { callOutline, documentTextOutline } from "ionicons/icons";
|
||||||
|
|
||||||
|
import '../Cadastro/Cadastro.css'
|
||||||
|
import { Color } from "@ionic/react/node_modules/@ionic/core";
|
||||||
|
|
||||||
|
interface cardItem {
|
||||||
|
icon: string;
|
||||||
|
label: string;
|
||||||
|
description: string;
|
||||||
|
url: string;
|
||||||
|
required: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface userData {
|
||||||
|
name: string;
|
||||||
|
lastname: string;
|
||||||
|
email: string;
|
||||||
|
phone_number: string;
|
||||||
|
birth_date: string;
|
||||||
|
bio: string;
|
||||||
|
document_type: string;
|
||||||
|
document: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LocationState {
|
||||||
|
userData: userData;
|
||||||
|
|
||||||
|
redirectData?: {
|
||||||
|
showToastMessage: boolean;
|
||||||
|
toastColor: Color;
|
||||||
|
toastMessage: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let items: cardItem[] = [
|
||||||
|
{
|
||||||
|
icon: documentTextOutline,
|
||||||
|
label: 'Documento',
|
||||||
|
description: 'Cadastre seu documento para que seu perfil possa ser verificado',
|
||||||
|
url: '/perfil/completar/documento',
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: callOutline,
|
||||||
|
label: 'Informações de contato',
|
||||||
|
description: 'Cadastre seu número de telefone celular que para possam contatar você',
|
||||||
|
url: '/perfil/completar/telefone',
|
||||||
|
required: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const CadastroCompletar: React.FC = () => {
|
||||||
|
const history = useHistory();
|
||||||
|
const location = useLocation<LocationState>();
|
||||||
|
|
||||||
|
const [showToast, setShowToast] = useState(false);
|
||||||
|
const [toastMessage, setToastMessage] = useState('');
|
||||||
|
const [toastColor, setToastColor] = useState<Color>("primary");
|
||||||
|
|
||||||
|
const handleCardClick = (item: cardItem) => {
|
||||||
|
if (!item.required) return
|
||||||
|
|
||||||
|
history.push({ pathname: item.url, state: { userData: location.state.userData } });
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!location.state || !location.state.userData) {
|
||||||
|
history.push({ pathname: '/perfil' })
|
||||||
|
}
|
||||||
|
|
||||||
|
if (location.state && location.state.redirectData) {
|
||||||
|
const redirectData = location.state.redirectData
|
||||||
|
|
||||||
|
if (redirectData.showToastMessage) {
|
||||||
|
setToastColor(redirectData.toastColor)
|
||||||
|
setToastMessage(redirectData.toastMessage)
|
||||||
|
setShowToast(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!location.state.userData.document) items[0].required = true
|
||||||
|
if (!location.state.userData.phone_number) items[1].required = true
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IonPage>
|
||||||
|
<IonHeader>
|
||||||
|
<IonToolbar>
|
||||||
|
<IonTitle>Completar cadastro</IonTitle>
|
||||||
|
<IonButtons slot="start">
|
||||||
|
<IonBackButton defaultHref="/perfil" />
|
||||||
|
</IonButtons>
|
||||||
|
</IonToolbar>
|
||||||
|
</IonHeader>
|
||||||
|
|
||||||
|
<IonContent>
|
||||||
|
{ items.map((item, index) => {
|
||||||
|
return (
|
||||||
|
<IonCard button={item.required} key={index} onClick={() => { handleCardClick(item) }}>
|
||||||
|
<IonItem>
|
||||||
|
<IonIcon icon={item.icon} slot="start" />
|
||||||
|
<IonLabel>{item.label}</IonLabel>
|
||||||
|
</IonItem>
|
||||||
|
|
||||||
|
<IonCardContent>{item.description}</IonCardContent>
|
||||||
|
</IonCard>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
|
||||||
|
<IonToast
|
||||||
|
position="top"
|
||||||
|
color={toastColor}
|
||||||
|
isOpen={showToast}
|
||||||
|
onDidDismiss={() => setShowToast(false)}
|
||||||
|
message={toastMessage}
|
||||||
|
duration={2500}
|
||||||
|
/>
|
||||||
|
</IonContent>
|
||||||
|
</IonPage>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CadastroCompletar;
|
||||||
219
src/pages/CadastroCompletar/CompletarDocumento.tsx
Normal file
219
src/pages/CadastroCompletar/CompletarDocumento.tsx
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface userData {
|
||||||
|
name: string;
|
||||||
|
lastname: string;
|
||||||
|
email: string;
|
||||||
|
phone_number: string;
|
||||||
|
birth_date: string;
|
||||||
|
bio: string;
|
||||||
|
document_type: string;
|
||||||
|
document: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LocationState {
|
||||||
|
userData: userData;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CompletarDocumento: React.FC = () => {
|
||||||
|
const location = useLocation<LocationState>();
|
||||||
|
|
||||||
|
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 validateform = () => {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
history.push({ pathname: '/perfil', state: {
|
||||||
|
redirectData: {
|
||||||
|
showToastMessage: true,
|
||||||
|
toastColor: "success",
|
||||||
|
toastMessage: response.message,
|
||||||
|
}
|
||||||
|
}})
|
||||||
|
}).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(() => {
|
||||||
|
if (!location.state.userData) {
|
||||||
|
history.push({ pathname: '/perfil', state: {
|
||||||
|
redirectData: {
|
||||||
|
showToastMessage: true,
|
||||||
|
toastColor: "warning",
|
||||||
|
toastMessage: "Houve um erro. Por favor, tente novamente.",
|
||||||
|
}, userData: location.state.userData
|
||||||
|
}})
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
position="top"
|
||||||
|
color='danger'
|
||||||
|
isOpen={showToast}
|
||||||
|
onDidDismiss={() => setShowToast(false)}
|
||||||
|
message={messageToast}
|
||||||
|
duration={2500}
|
||||||
|
/>
|
||||||
|
</IonContent>
|
||||||
|
</IonPage>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CompletarDocumento;
|
||||||
|
|
||||||
169
src/pages/CadastroCompletar/CompletarTelefone.tsx
Normal file
169
src/pages/CadastroCompletar/CompletarTelefone.tsx
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
import {
|
||||||
|
IonBackButton,
|
||||||
|
IonButtons,
|
||||||
|
IonContent,
|
||||||
|
IonFab,
|
||||||
|
IonFabButton,
|
||||||
|
IonHeader,
|
||||||
|
IonIcon,
|
||||||
|
IonList,
|
||||||
|
IonPage,
|
||||||
|
IonTitle,
|
||||||
|
IonToast,
|
||||||
|
IonToolbar
|
||||||
|
} from "@ionic/react";
|
||||||
|
import React, { useState, useEffect } 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 { Color } from "@ionic/react/node_modules/@ionic/core";
|
||||||
|
|
||||||
|
interface documentTypesInterface {
|
||||||
|
label: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface userData {
|
||||||
|
name: string;
|
||||||
|
lastname: string;
|
||||||
|
email: string;
|
||||||
|
phone_number: string;
|
||||||
|
birth_date: string;
|
||||||
|
bio: string;
|
||||||
|
document_type: string;
|
||||||
|
document: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LocationState {
|
||||||
|
userData: userData;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CompletarTelefone: React.FC = () => {
|
||||||
|
const location = useLocation<LocationState>();
|
||||||
|
|
||||||
|
const [hasChangedSinceInitialState, setHasChangedSinceInitialState] = useState(false);
|
||||||
|
|
||||||
|
const [phone, setPhone] = useState('');
|
||||||
|
|
||||||
|
const [showToast, setShowToast] = useState(false);
|
||||||
|
const [messageToast, setMessageToast] = useState('');
|
||||||
|
const [toastColor, setToastColor] = useState<Color>("primary");
|
||||||
|
|
||||||
|
const history = useHistory();
|
||||||
|
|
||||||
|
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') {
|
||||||
|
setToastColor("warning")
|
||||||
|
setMessageToast(response.message);
|
||||||
|
setShowToast(true);
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const userDataObj = location.state.userData
|
||||||
|
|
||||||
|
history.push({ pathname: '/perfil', state: {
|
||||||
|
redirectData: {
|
||||||
|
showToastMessage: true,
|
||||||
|
toastColor: "success",
|
||||||
|
toastMessage: response.message,
|
||||||
|
},
|
||||||
|
}})
|
||||||
|
}).catch((err) => {
|
||||||
|
setMessageToast(err);
|
||||||
|
setShowToast(true);
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!location.state.userData) {
|
||||||
|
history.push({ pathname: '/perfil', state: {
|
||||||
|
redirectData: {
|
||||||
|
showToastMessage: true,
|
||||||
|
toastColor: "warning",
|
||||||
|
toastMessage: "Houve um erro. Por favor, tente novamente.",
|
||||||
|
}
|
||||||
|
}})
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
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
|
||||||
|
position="top"
|
||||||
|
color={toastColor}
|
||||||
|
isOpen={showToast}
|
||||||
|
onDidDismiss={() => setShowToast(false)}
|
||||||
|
message={messageToast}
|
||||||
|
duration={2500}
|
||||||
|
/>
|
||||||
|
</IonContent>
|
||||||
|
</IonPage>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CompletarTelefone;
|
||||||
|
|
||||||
0
src/pages/CadastroVan.css
Normal file
0
src/pages/CadastroVan.css
Normal file
355
src/pages/CadastroVan.tsx
Normal file
355
src/pages/CadastroVan.tsx
Normal file
@@ -0,0 +1,355 @@
|
|||||||
|
import {
|
||||||
|
IonToast,
|
||||||
|
IonItem,
|
||||||
|
IonLabel,
|
||||||
|
IonInput,
|
||||||
|
IonBackButton,
|
||||||
|
IonButton,
|
||||||
|
IonButtons,
|
||||||
|
IonContent,
|
||||||
|
IonHeader,
|
||||||
|
IonPage,
|
||||||
|
IonToolbar,
|
||||||
|
IonTitle,
|
||||||
|
IonList,
|
||||||
|
IonCheckbox,
|
||||||
|
IonListHeader,
|
||||||
|
IonSelect,
|
||||||
|
IonSelectOption,
|
||||||
|
} from "@ionic/react";
|
||||||
|
|
||||||
|
import React, { useEffect, useReducer, useState } from "react";
|
||||||
|
import { useHistory } from "react-router-dom";
|
||||||
|
|
||||||
|
// import * as yup from 'yup';
|
||||||
|
|
||||||
|
import carsService from '../services/functions/carsService'
|
||||||
|
|
||||||
|
import * as vansRoutes from '../services/api/vans';
|
||||||
|
|
||||||
|
import "./CadastroVan.css";
|
||||||
|
import { Color } from "@ionic/react/node_modules/@ionic/core";
|
||||||
|
|
||||||
|
const CadastroVan: React.FC = () => {
|
||||||
|
const history = useHistory();
|
||||||
|
|
||||||
|
const [showToast, setShowToast] = useState<boolean>(false);
|
||||||
|
const [toastMessage, setToastMessage] = useState<string>("");
|
||||||
|
const [toastColor, setToastColor] = useState<Color>("primary");
|
||||||
|
|
||||||
|
const [carModels, setCarModels] = useState([{
|
||||||
|
id_model: '',
|
||||||
|
name: ''
|
||||||
|
}]);
|
||||||
|
|
||||||
|
const [inputValues, setInputValues] = useReducer(
|
||||||
|
(state: any, newState: any) => ({ ...state, ...newState }),
|
||||||
|
{
|
||||||
|
carPlate: '',
|
||||||
|
carBrand: '',
|
||||||
|
carModel: '',
|
||||||
|
seats_number: 1,
|
||||||
|
isRented: false,
|
||||||
|
locator_name: '',
|
||||||
|
locator_address: '',
|
||||||
|
locator_complement: '',
|
||||||
|
locator_city: '',
|
||||||
|
locator_state: '',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const clearRentalData = () => {
|
||||||
|
setInputValues({
|
||||||
|
carRentalName: '',
|
||||||
|
complement: '',
|
||||||
|
city: '',
|
||||||
|
state: '',
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateForm = (): boolean => {
|
||||||
|
const vanForm = {
|
||||||
|
carPlate: inputValues.carPlate,
|
||||||
|
carBrand: inputValues.carBrand,
|
||||||
|
carModel: inputValues.carModel,
|
||||||
|
seats_number: inputValues.seats_number,
|
||||||
|
isRented: inputValues.isRented,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (
|
||||||
|
!vanForm.carPlate ||
|
||||||
|
vanForm.carPlate.length !== 7 ||
|
||||||
|
!vanForm.carPlate.match(/([A-z0-9]){7}/g)
|
||||||
|
) {
|
||||||
|
setToastMessage("Placa do veículo inválida!");
|
||||||
|
setShowToast(true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!vanForm.carBrand) {
|
||||||
|
setToastMessage("Marca do veículo é obrigatório");
|
||||||
|
setShowToast(true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!vanForm.carModel) {
|
||||||
|
setToastMessage("Modelo do veículo é obrigatório");
|
||||||
|
setShowToast(true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!vanForm.seats_number || !parseInt(`${vanForm.seats_number}`)) {
|
||||||
|
setToastMessage("Número de passageiros inválido");
|
||||||
|
setShowToast(true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((Number)(vanForm.seats_number) < 1) {
|
||||||
|
setToastMessage("Número de passageiros deve ser positivo!");
|
||||||
|
setShowToast(true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vanForm.isRented) {
|
||||||
|
return validateRentalForm();
|
||||||
|
} else {
|
||||||
|
clearRentalData();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateRentalForm = (): boolean => {
|
||||||
|
const locatorForm = {
|
||||||
|
locator_name: inputValues.locator_name,
|
||||||
|
locator_address: inputValues.locator_address,
|
||||||
|
locator_complement: inputValues.locator_complement,
|
||||||
|
locator_city: inputValues.locator_city,
|
||||||
|
locator_state: inputValues.locator_state,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!locatorForm.locator_name) {
|
||||||
|
setToastMessage("Nome do Locador é obrigatório");
|
||||||
|
setShowToast(true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!locatorForm.locator_city ||
|
||||||
|
!locatorForm.locator_city.match(/([A-zà-úÀ-Ú])/g)
|
||||||
|
) {
|
||||||
|
setToastMessage("Cidade inválida");
|
||||||
|
setShowToast(true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!locatorForm.locator_state ||
|
||||||
|
!locatorForm.locator_state.match(/([A-zà-úÀ-Ú])/g)
|
||||||
|
) {
|
||||||
|
setToastMessage("Estado inválido");
|
||||||
|
setShowToast(true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
if (!validateForm()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// cria registro da van
|
||||||
|
await vansRoutes.create({
|
||||||
|
plate: inputValues.carPlate,
|
||||||
|
brand: inputValues.carBrand,
|
||||||
|
model: inputValues.carModel,
|
||||||
|
seats_number: inputValues.seats_number,
|
||||||
|
locator_name: inputValues.locator_name,
|
||||||
|
locator_address: inputValues.locator_address,
|
||||||
|
locator_complement: inputValues.locator_complement,
|
||||||
|
locator_city: inputValues.locator_city,
|
||||||
|
locator_state: inputValues.locator_state
|
||||||
|
}).then(response => {
|
||||||
|
if (response.status === 'error') {
|
||||||
|
setToastMessage(response.message);
|
||||||
|
setShowToast(true);
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
history.push({ pathname: '/perfil', state: {
|
||||||
|
redirectData: {
|
||||||
|
showToastMessage: true,
|
||||||
|
toastColor: "success",
|
||||||
|
toastMessage: response.message,
|
||||||
|
},
|
||||||
|
}})
|
||||||
|
}).catch((err) => {
|
||||||
|
setToastColor("danger")
|
||||||
|
setToastMessage(err);
|
||||||
|
setShowToast(true);
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let isMounted = true
|
||||||
|
|
||||||
|
const getCarsModels = async () => {
|
||||||
|
const carModelsRes = await carsService.getAllCarModels()
|
||||||
|
|
||||||
|
if (carModelsRes.error) {
|
||||||
|
setToastColor("danger")
|
||||||
|
setToastMessage(carModelsRes.error.errorMessage);
|
||||||
|
setShowToast(true);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (carModelsRes.data) {
|
||||||
|
if (isMounted) {
|
||||||
|
setCarModels(carModelsRes.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getCarsModels()
|
||||||
|
|
||||||
|
return () => { isMounted = false }
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IonPage>
|
||||||
|
<IonHeader>
|
||||||
|
<IonToolbar>
|
||||||
|
<IonTitle>Cadastro de veículo</IonTitle>
|
||||||
|
<IonButtons slot='start'>
|
||||||
|
<IonBackButton defaultHref='/perfil' />
|
||||||
|
</IonButtons>
|
||||||
|
</IonToolbar>
|
||||||
|
</IonHeader>
|
||||||
|
|
||||||
|
<IonContent>
|
||||||
|
<IonList lines="full" class="ion-no-margin">
|
||||||
|
<IonListHeader lines="full">
|
||||||
|
<IonLabel>Informações do veículo</IonLabel>
|
||||||
|
</IonListHeader>
|
||||||
|
<IonItem>
|
||||||
|
<IonLabel position='floating'>Placa </IonLabel>
|
||||||
|
<IonInput
|
||||||
|
type='text'
|
||||||
|
clearInput
|
||||||
|
maxlength={7}
|
||||||
|
placeholder='Digite a Placa do Veículo'
|
||||||
|
onIonChange={(e: any) => setInputValues({ carPlate: e.target.value })}
|
||||||
|
/>
|
||||||
|
</IonItem>
|
||||||
|
|
||||||
|
{/* TODO, problema de setState para valores vindos de um evento sendo triggerado por um ion-select */}
|
||||||
|
<IonItem>
|
||||||
|
<IonLabel>Marca</IonLabel>
|
||||||
|
<IonSelect onIonChange={(e: any) => { setInputValues({ carBrand: e.detail.value }) }}>
|
||||||
|
{ carModels ? carModels.map((carModel, index) => {
|
||||||
|
return (<IonSelectOption key={index} value={carModel.name}>{carModel.name}</IonSelectOption>)
|
||||||
|
}) : <></> }
|
||||||
|
</IonSelect>
|
||||||
|
</IonItem>
|
||||||
|
|
||||||
|
<IonItem>
|
||||||
|
<IonLabel position='floating'>Modelo </IonLabel>
|
||||||
|
<IonInput
|
||||||
|
type='text'
|
||||||
|
clearInput
|
||||||
|
placeholder='Digite o Modelo do Veículo'
|
||||||
|
onIonChange={(e: any) => setInputValues({ carModel: e.target.value })}
|
||||||
|
/>
|
||||||
|
</IonItem>
|
||||||
|
|
||||||
|
<IonItem>
|
||||||
|
<IonLabel position='floating'>
|
||||||
|
Número de assentos
|
||||||
|
</IonLabel>
|
||||||
|
<IonInput
|
||||||
|
type='number'
|
||||||
|
min={1}
|
||||||
|
clearInput
|
||||||
|
placeholder='podem ser ocupados por passageiros'
|
||||||
|
onIonChange={(e: any) => setInputValues({ seats_number: e.target.value })}
|
||||||
|
/>
|
||||||
|
</IonItem>
|
||||||
|
</IonList>
|
||||||
|
|
||||||
|
<IonList lines="full" class="ion-no-margin">
|
||||||
|
<IonListHeader lines="full">
|
||||||
|
<IonLabel>Informações do locador</IonLabel>
|
||||||
|
</IonListHeader>
|
||||||
|
|
||||||
|
<IonItem>
|
||||||
|
<IonLabel>O veículo é alugado?</IonLabel>
|
||||||
|
<IonCheckbox checked={inputValues.isRented} onIonChange={e => setInputValues({ isRented: e.detail.checked })} />
|
||||||
|
</IonItem>
|
||||||
|
|
||||||
|
{inputValues.isRented && (
|
||||||
|
<div>
|
||||||
|
<IonItem>
|
||||||
|
<IonLabel position="stacked" />
|
||||||
|
<IonInput
|
||||||
|
type='text'
|
||||||
|
clearInput
|
||||||
|
placeholder='Nome completo do Locador'
|
||||||
|
onIonChange={(e: any) => setInputValues({ locator_name: e.target.value })}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<IonInput
|
||||||
|
type='text'
|
||||||
|
clearInput
|
||||||
|
placeholder='Endereço do locador'
|
||||||
|
onIonChange={(e: any) => setInputValues({ locator_address: e.target.value })}
|
||||||
|
/>
|
||||||
|
<IonInput
|
||||||
|
type='text'
|
||||||
|
clearInput
|
||||||
|
placeholder='Complemento'
|
||||||
|
onIonChange={(e: any) => setInputValues({ locator_complement: e.target.value })}
|
||||||
|
/>
|
||||||
|
<IonInput
|
||||||
|
type='text'
|
||||||
|
clearInput
|
||||||
|
placeholder='Cidade'
|
||||||
|
onIonChange={(e: any) => setInputValues({ locator_city: e.target.value })}
|
||||||
|
/>
|
||||||
|
<IonInput
|
||||||
|
type='text'
|
||||||
|
clearInput
|
||||||
|
placeholder='Estado'
|
||||||
|
onIonChange={(e: any) => setInputValues({ locator_state: e.target.value })}
|
||||||
|
/>
|
||||||
|
</IonItem>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<IonButton
|
||||||
|
className='ion-margin-top'
|
||||||
|
expand='block'
|
||||||
|
onClick={handleSubmit}
|
||||||
|
>
|
||||||
|
Salvar
|
||||||
|
</IonButton>
|
||||||
|
</div>
|
||||||
|
</IonList>
|
||||||
|
|
||||||
|
<IonToast
|
||||||
|
position="top"
|
||||||
|
color={toastColor}
|
||||||
|
isOpen={showToast}
|
||||||
|
onDidDismiss={() => setShowToast(false)}
|
||||||
|
message={toastMessage}
|
||||||
|
duration={2500}
|
||||||
|
/>
|
||||||
|
</IonContent>
|
||||||
|
</IonPage>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CadastroVan;
|
||||||
@@ -1,56 +1,74 @@
|
|||||||
import { IonItem, IonLabel, IonInput, IonButton, IonCardTitle, IonCol, IonContent, IonGrid, IonPage, IonRow } from '@ionic/react';
|
import { IonContent, IonPage, IonToast } from '@ionic/react';
|
||||||
import { Action } from '../components/Action';
|
import { Color } from '@ionic/react/node_modules/@ionic/core';
|
||||||
|
import { useContext, useEffect, useState } from 'react';
|
||||||
|
import { useLocation } from 'react-router';
|
||||||
|
|
||||||
|
import { UserContext } from '../App';
|
||||||
|
|
||||||
|
import * as sessionRoutes from '../services/api/session';
|
||||||
|
|
||||||
|
interface LocationState {
|
||||||
|
redirectData?: {
|
||||||
|
showToastMessage: boolean;
|
||||||
|
toastColor: Color;
|
||||||
|
toastMessage: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const Home: React.FC = () => {
|
const Home: React.FC = () => {
|
||||||
|
const location = useLocation<LocationState>();
|
||||||
|
|
||||||
|
const user = useContext(UserContext);
|
||||||
|
|
||||||
|
const [showToast, setShowToast] = useState(false);
|
||||||
|
const [toastMessage, setToastMessage] = useState('');
|
||||||
|
const [toastColor, setToastColor] = useState<Color>("primary");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (location.state && location.state.redirectData) {
|
||||||
|
const redirectData = location.state.redirectData
|
||||||
|
|
||||||
|
if (redirectData.showToastMessage) {
|
||||||
|
setToastColor(redirectData.toastColor)
|
||||||
|
setToastMessage(redirectData.toastMessage)
|
||||||
|
setShowToast(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const refreshUserToken = async () => {
|
||||||
|
await sessionRoutes.refresh().then(response => {
|
||||||
|
if (response.status === 'error') {
|
||||||
|
// setMessageToast(response.message);
|
||||||
|
// setShowToast(true);
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user.setIsLoggedIn(true);
|
||||||
|
}).catch(error => {
|
||||||
|
// if (!error.response) return
|
||||||
|
|
||||||
|
// se o backend retornou uma mensagem de erro customizada
|
||||||
|
// if (error.response.data.message) {
|
||||||
|
console.dir('Houve um erro: ', { error })
|
||||||
|
alert('Houve um erro')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshUserToken()
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IonPage>
|
<IonPage>
|
||||||
<IonContent fullscreen>
|
<IonContent>
|
||||||
<IonGrid className="ion-padding">
|
<IonToast
|
||||||
<IonRow>
|
position="top"
|
||||||
<IonCol size="12">
|
color={toastColor}
|
||||||
<IonCardTitle>Como você deseja se cadastrar?</IonCardTitle>
|
isOpen={showToast}
|
||||||
</IonCol>
|
onDidDismiss={() => setShowToast(false)}
|
||||||
</IonRow>
|
message={toastMessage}
|
||||||
<IonRow>
|
duration={2500}
|
||||||
<IonCol size="12">
|
/>
|
||||||
<div id='nome-sobrenome'>
|
|
||||||
<IonItem>
|
|
||||||
<IonLabel position='floating'>Nome</IonLabel>
|
|
||||||
<IonInput clearInput></IonInput>
|
|
||||||
</IonItem>
|
|
||||||
<IonItem>
|
|
||||||
<IonLabel position='floating'>Sobrenome</IonLabel>
|
|
||||||
<IonInput clearInput></IonInput>
|
|
||||||
</IonItem>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<IonItem>
|
|
||||||
<IonLabel position='floating'>E-mail</IonLabel>
|
|
||||||
<IonInput clearInput type='email'></IonInput>
|
|
||||||
</IonItem>
|
|
||||||
|
|
||||||
<IonItem>
|
|
||||||
<IonLabel position='stacked'>Data de nascimento</IonLabel>
|
|
||||||
<IonInput type='date'></IonInput>
|
|
||||||
</IonItem>
|
|
||||||
|
|
||||||
<IonItem>
|
|
||||||
<IonLabel position='floating'>Senha</IonLabel>
|
|
||||||
<IonInput clearInput type='password'></IonInput>
|
|
||||||
</IonItem>
|
|
||||||
<IonItem>
|
|
||||||
<IonLabel position='floating'>Confirme a senha</IonLabel>
|
|
||||||
<IonInput clearInput type='password'></IonInput>
|
|
||||||
</IonItem>
|
|
||||||
|
|
||||||
<IonButton className="ion-margin-top" expand="block">Cadastrar-se</IonButton>
|
|
||||||
</IonCol>
|
|
||||||
</IonRow>
|
|
||||||
<small className='ion-margin-top'>
|
|
||||||
Ao se cadastrar, você aceita nossos <a href="">Termos e Condições</a> e nossa <a href=""> Política de Privacidade</a>.
|
|
||||||
</small>
|
|
||||||
<Action message="Já tem conta?" text="Login" link="/login" />
|
|
||||||
</IonGrid>
|
|
||||||
</IonContent>
|
</IonContent>
|
||||||
</IonPage>
|
</IonPage>
|
||||||
);
|
);
|
||||||
|
|||||||
174
src/pages/Login.tsx
Normal file
174
src/pages/Login.tsx
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
import {
|
||||||
|
IonContent,
|
||||||
|
IonHeader,
|
||||||
|
IonPage,
|
||||||
|
IonTitle,
|
||||||
|
IonToolbar
|
||||||
|
} from "@ionic/react";
|
||||||
|
import React, { useContext, useState } from "react";
|
||||||
|
import { IonGrid, IonRow, IonCol, IonToast } from "@ionic/react";
|
||||||
|
import { useHistory } from "react-router-dom";
|
||||||
|
import {
|
||||||
|
IonItem,
|
||||||
|
IonLabel,
|
||||||
|
IonInput,
|
||||||
|
IonButton,
|
||||||
|
} from "@ionic/react";
|
||||||
|
|
||||||
|
import * as sessionRoutes from '../services/api/session';
|
||||||
|
import LocalStorage from '../LocalStorage';
|
||||||
|
import { Action } from "../components/Action";
|
||||||
|
import { UserContext } from "../App";
|
||||||
|
|
||||||
|
const Page: React.FC = () => {
|
||||||
|
const [showToast, setShowToast] = useState(false);
|
||||||
|
const [messageToast, setMessageToast ] = useState('');
|
||||||
|
|
||||||
|
const history = useHistory();
|
||||||
|
const [email, setEmail] = useState('');
|
||||||
|
const [password, setPassword] = useState('');
|
||||||
|
|
||||||
|
const user = useContext(UserContext);
|
||||||
|
|
||||||
|
function validateEmail(email: string) {
|
||||||
|
const re =
|
||||||
|
// eslint-disable-next-line no-control-regex
|
||||||
|
/^((?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\]))$/;
|
||||||
|
return re.test(String(email).toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
const validateForm = () => {
|
||||||
|
if (!email) {
|
||||||
|
setMessageToast("Por favor, informe o e-mail");
|
||||||
|
setShowToast(true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!validateEmail(email)) {
|
||||||
|
setMessageToast("E-mail inválido");
|
||||||
|
setShowToast(true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!password) {
|
||||||
|
setMessageToast("Por favor, digite a sua senha");
|
||||||
|
setShowToast(true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(password.length < 7 || password.length > 12) {
|
||||||
|
setMessageToast("A senha deve conter entre 7 e 12 dígitos");
|
||||||
|
setShowToast(true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleLogin = async () => {
|
||||||
|
if (!validateForm()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const singinForm = {
|
||||||
|
login: email,
|
||||||
|
password: password,
|
||||||
|
};
|
||||||
|
|
||||||
|
await sessionRoutes.create(singinForm).then(response => {
|
||||||
|
if (response.status === 'error') {
|
||||||
|
setMessageToast(response.message);
|
||||||
|
setShowToast(true);
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const { token } = response.token
|
||||||
|
|
||||||
|
LocalStorage.setToken(token);
|
||||||
|
|
||||||
|
user.setIsLoggedIn(true);
|
||||||
|
|
||||||
|
history.push({ pathname: '/home', state: { redirectData: {
|
||||||
|
showToastMessage: true,
|
||||||
|
toastColor: "success",
|
||||||
|
toastMessage: "Usuário autenticado com sucesso!",
|
||||||
|
}}})
|
||||||
|
}).catch(error => {
|
||||||
|
// if (!error.response) return
|
||||||
|
|
||||||
|
// se o backend retornou uma mensagem de erro customizada
|
||||||
|
// if (error.response.data.message) {
|
||||||
|
console.dir('Houve um erro: ', { error })
|
||||||
|
alert('Houve um erro')
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IonPage>
|
||||||
|
<IonHeader>
|
||||||
|
<IonToolbar>
|
||||||
|
<IonTitle>Login</IonTitle>
|
||||||
|
</IonToolbar>
|
||||||
|
</IonHeader>
|
||||||
|
|
||||||
|
<IonContent fullscreen>
|
||||||
|
<IonHeader collapse="condense">
|
||||||
|
<IonToolbar>
|
||||||
|
<IonTitle size="large">Login</IonTitle>
|
||||||
|
</IonToolbar>
|
||||||
|
</IonHeader>
|
||||||
|
|
||||||
|
<IonGrid>
|
||||||
|
<IonRow>
|
||||||
|
<IonCol>
|
||||||
|
<IonItem>
|
||||||
|
<IonLabel position="floating"> Email</IonLabel>
|
||||||
|
<IonInput
|
||||||
|
type="email"
|
||||||
|
value={email}
|
||||||
|
onIonChange={(e) => setEmail(e.detail.value!)}
|
||||||
|
></IonInput>
|
||||||
|
</IonItem>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
|
||||||
|
<IonRow>
|
||||||
|
<IonCol>
|
||||||
|
<IonItem>
|
||||||
|
<IonLabel position="floating"> Senha</IonLabel>
|
||||||
|
<IonInput
|
||||||
|
type="password"
|
||||||
|
value={password}
|
||||||
|
onIonChange={(e) => setPassword(e.detail.value!)}
|
||||||
|
></IonInput>
|
||||||
|
</IonItem>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
|
||||||
|
<IonRow>
|
||||||
|
<IonCol>
|
||||||
|
<IonButton expand="block" onClick={handleLogin}>
|
||||||
|
Login
|
||||||
|
</IonButton>
|
||||||
|
<p style={{ fontSize: "medium" }}>
|
||||||
|
<Action message="Ainda não possui uma conta?" text="Cadastre-se aqui!" link="/cadastro" />
|
||||||
|
</p>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
</IonGrid>
|
||||||
|
|
||||||
|
<IonToast
|
||||||
|
position="top"
|
||||||
|
color='danger'
|
||||||
|
isOpen={showToast}
|
||||||
|
onDidDismiss={() => setShowToast(false)}
|
||||||
|
message={messageToast}
|
||||||
|
duration={2500}
|
||||||
|
/>
|
||||||
|
</IonContent>
|
||||||
|
</IonPage>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Page;
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
import React, { useState, useRef } from 'react';
|
|
||||||
import {
|
|
||||||
IonTabs,
|
|
||||||
IonTabBar,
|
|
||||||
IonTabButton,
|
|
||||||
IonLabel,
|
|
||||||
IonPage,
|
|
||||||
IonIcon,
|
|
||||||
IonRouterOutlet
|
|
||||||
} from '@ionic/react';
|
|
||||||
import { Redirect, Route } from 'react-router-dom';
|
|
||||||
import { IonReactRouter } from '@ionic/react-router';
|
|
||||||
import { search, home, person } from 'ionicons/icons';
|
|
||||||
|
|
||||||
import Home from './Home';
|
|
||||||
|
|
||||||
export const MainPages: React.FC = () => {
|
|
||||||
|
|
||||||
return (
|
|
||||||
<IonTabs>
|
|
||||||
<IonRouterOutlet>
|
|
||||||
<Route path="/home" exact={true}>
|
|
||||||
<Home />
|
|
||||||
</Route>
|
|
||||||
<Route path="/mainpages" render={() => <Redirect to="/home" />} />
|
|
||||||
</IonRouterOutlet>
|
|
||||||
|
|
||||||
<IonTabBar slot="bottom">
|
|
||||||
<IonTabButton tab="buscar" href="/buscar">
|
|
||||||
<IonIcon icon={search} />
|
|
||||||
<IonLabel>Buscar</IonLabel>
|
|
||||||
</IonTabButton>
|
|
||||||
<IonTabButton tab="home" href="/home">
|
|
||||||
<IonIcon icon={home} />
|
|
||||||
<IonLabel>Home</IonLabel>
|
|
||||||
</IonTabButton>
|
|
||||||
<IonTabButton tab="perfil" href="/perfil">
|
|
||||||
<IonIcon icon={person} />
|
|
||||||
<IonLabel>Perfil</IonLabel>
|
|
||||||
</IonTabButton>
|
|
||||||
</IonTabBar>
|
|
||||||
</IonTabs>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MainPages;
|
|
||||||
118
src/pages/MinhasVans.tsx
Normal file
118
src/pages/MinhasVans.tsx
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
import { IonBackButton, IonButtons, IonCard, IonCardContent, IonCardHeader, IonCardSubtitle, IonCardTitle, IonContent, IonHeader, IonIcon, IonItem, IonLabel, IonPage, IonTitle, IonToast, IonToolbar } from '@ionic/react';
|
||||||
|
import { Color } from '@ionic/react/node_modules/@ionic/core';
|
||||||
|
import { carOutline } from 'ionicons/icons';
|
||||||
|
import { useContext, useEffect, useState } from 'react';
|
||||||
|
import { useHistory, useLocation } from 'react-router';
|
||||||
|
|
||||||
|
import { UserContext } from '../App';
|
||||||
|
|
||||||
|
import * as vansRoutes from '../services/api/vans';
|
||||||
|
|
||||||
|
import sessionsService from '../services/functions/sessionsService'
|
||||||
|
|
||||||
|
interface VanInfo {
|
||||||
|
plate: string;
|
||||||
|
brand: string;
|
||||||
|
model: string;
|
||||||
|
seats_number: string;
|
||||||
|
document_status: boolean,
|
||||||
|
locator_name: string;
|
||||||
|
locator_address: string;
|
||||||
|
locator_complement: string;
|
||||||
|
locator_city: string;
|
||||||
|
locator_state: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MinhasVans: React.FC = () => {
|
||||||
|
const history = useHistory();
|
||||||
|
|
||||||
|
const [showToast, setShowToast] = useState(false);
|
||||||
|
const [toastMessage, setToastMessage] = useState('');
|
||||||
|
const [toastColor, setToastColor] = useState<Color>("primary");
|
||||||
|
|
||||||
|
const [userVans, setUserVans] = useState<VanInfo[]>();
|
||||||
|
|
||||||
|
const redirectUserToLogin = () => {
|
||||||
|
history.push({ pathname: '/login' });
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const getUserVans = async () => {
|
||||||
|
let userId = ''
|
||||||
|
|
||||||
|
const refreshSessionRes = await sessionsService.refreshSession()
|
||||||
|
|
||||||
|
if (refreshSessionRes.error) {
|
||||||
|
redirectUserToLogin()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (refreshSessionRes.userId) {
|
||||||
|
userId = refreshSessionRes.userId
|
||||||
|
}
|
||||||
|
|
||||||
|
vansRoutes.getByUserId(userId).then(response => {
|
||||||
|
if (response.status === 'error') {
|
||||||
|
setToastColor("danger")
|
||||||
|
setToastMessage(response.message);
|
||||||
|
setShowToast(true);
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setUserVans(response.data)
|
||||||
|
}).catch((err) => {
|
||||||
|
setToastColor("danger")
|
||||||
|
setToastMessage(err);
|
||||||
|
setShowToast(true);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getUserVans()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IonPage>
|
||||||
|
<IonHeader>
|
||||||
|
<IonToolbar>
|
||||||
|
<IonTitle>Minhas vans</IonTitle>
|
||||||
|
<IonButtons slot="start">
|
||||||
|
<IonBackButton defaultHref="/perfil" />
|
||||||
|
</IonButtons>
|
||||||
|
</IonToolbar>
|
||||||
|
</IonHeader>
|
||||||
|
|
||||||
|
<IonContent>
|
||||||
|
{ userVans ? userVans.map((van, index) => {
|
||||||
|
return (
|
||||||
|
<IonCard key={index}>
|
||||||
|
<IonCardHeader>
|
||||||
|
<IonCardTitle>{van.plate}</IonCardTitle>
|
||||||
|
<IonCardSubtitle>{van.brand} - {van.model}</IonCardSubtitle>
|
||||||
|
</IonCardHeader>
|
||||||
|
{ van.locator_name ?
|
||||||
|
<>
|
||||||
|
<IonCardContent>{van.seats_number} assentos - Locador: {van.locator_name}</IonCardContent>
|
||||||
|
</> :
|
||||||
|
<>
|
||||||
|
<IonCardContent>{van.seats_number} assentos - Não é alugado</IonCardContent>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
</IonCard>
|
||||||
|
)
|
||||||
|
}) : <></>}
|
||||||
|
|
||||||
|
<IonToast
|
||||||
|
position="top"
|
||||||
|
color={toastColor}
|
||||||
|
isOpen={showToast}
|
||||||
|
onDidDismiss={() => setShowToast(false)}
|
||||||
|
message={toastMessage}
|
||||||
|
duration={2500}
|
||||||
|
/>
|
||||||
|
</IonContent>
|
||||||
|
</IonPage>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MinhasVans;
|
||||||
16
src/pages/Perfil.css
Normal file
16
src/pages/Perfil.css
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#avatar {
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
display: block;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#profile-status {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
IonIcon {
|
||||||
|
color: blue;
|
||||||
|
}
|
||||||
328
src/pages/Perfil.tsx
Normal file
328
src/pages/Perfil.tsx
Normal file
@@ -0,0 +1,328 @@
|
|||||||
|
import {
|
||||||
|
IonBackButton,
|
||||||
|
IonBadge,
|
||||||
|
IonButtons,
|
||||||
|
IonCard,
|
||||||
|
IonCardContent,
|
||||||
|
IonCardHeader,
|
||||||
|
IonCardTitle,
|
||||||
|
IonChip,
|
||||||
|
IonContent,
|
||||||
|
IonHeader,
|
||||||
|
IonIcon,
|
||||||
|
IonItem,
|
||||||
|
IonLabel,
|
||||||
|
IonList,
|
||||||
|
IonListHeader,
|
||||||
|
IonPage,
|
||||||
|
IonTitle,
|
||||||
|
IonToast,
|
||||||
|
IonToolbar,
|
||||||
|
} from "@ionic/react";
|
||||||
|
import { useHistory, useLocation } from "react-router-dom";
|
||||||
|
import React, { useState, useEffect, useReducer, useContext } from "react";
|
||||||
|
import { callOutline, cardOutline, carOutline, createOutline, exitOutline, logoFacebook, logoWhatsapp, personOutline, shieldCheckmarkOutline, starOutline } from "ionicons/icons";
|
||||||
|
|
||||||
|
import './Perfil.css'
|
||||||
|
import LocalStorage from "../LocalStorage";
|
||||||
|
|
||||||
|
import sessionsService from '../services/functions/sessionsService'
|
||||||
|
import usersService from '../services/functions/usersService'
|
||||||
|
import { UserContext } from "../App";
|
||||||
|
import { Color } from "@ionic/react/node_modules/@ionic/core";
|
||||||
|
|
||||||
|
interface ScanNewProps {
|
||||||
|
match: {
|
||||||
|
params: {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LocationState {
|
||||||
|
redirectData?: {
|
||||||
|
showToastMessage: boolean;
|
||||||
|
toastColor: Color;
|
||||||
|
toastMessage: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Perfil: React.FC<ScanNewProps> = (props) => {
|
||||||
|
const user = useContext(UserContext);
|
||||||
|
|
||||||
|
const history = useHistory();
|
||||||
|
const location = useLocation<LocationState>();
|
||||||
|
|
||||||
|
const [isVisitor, setIsVisitor] = useState(true)
|
||||||
|
const [isDriver, setIsDriver] = useState(false)
|
||||||
|
|
||||||
|
const [incompleteProfile, setIncompleteProfile] = useState(false)
|
||||||
|
const [incompleteProfileCounter, setIncompleteProfileCounter] = useState(0)
|
||||||
|
|
||||||
|
const [showToast, setShowToast] = useState(false);
|
||||||
|
const [toastMessage, setToastMessage] = useState('');
|
||||||
|
const [toastColor, setToastColor] = useState<Color>("primary");
|
||||||
|
|
||||||
|
const [inputValues, setInputValues] = useReducer(
|
||||||
|
(state: any, newState: any) => ({ ...state, ...newState }),
|
||||||
|
{
|
||||||
|
id: '',
|
||||||
|
name: '',
|
||||||
|
lastname: '',
|
||||||
|
email: '',
|
||||||
|
phone_number: '',
|
||||||
|
birth_date: '',
|
||||||
|
bio: '',
|
||||||
|
document_type: '',
|
||||||
|
document: '',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const redirectUserToLogin = () => {
|
||||||
|
history.push({ pathname: '/login' });
|
||||||
|
setToastMessage("Por favor, autentique-se!");
|
||||||
|
setShowToast(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const logoff = () => {
|
||||||
|
LocalStorage.clearToken()
|
||||||
|
user.setIsLoggedIn(false);
|
||||||
|
history.push({ pathname: '/login' });
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (location.state && location.state.redirectData) {
|
||||||
|
const redirectData = location.state.redirectData
|
||||||
|
|
||||||
|
if (redirectData.showToastMessage) {
|
||||||
|
setToastColor(redirectData.toastColor)
|
||||||
|
setToastMessage(redirectData.toastMessage)
|
||||||
|
setShowToast(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadUserData = async () => {
|
||||||
|
let userId = ''
|
||||||
|
|
||||||
|
// verify if user is authenticated
|
||||||
|
if (props.match.params.id) {
|
||||||
|
userId = props.match.params.id
|
||||||
|
} else {
|
||||||
|
const refreshSessionRes = await sessionsService.refreshSession()
|
||||||
|
|
||||||
|
if (refreshSessionRes.error) {
|
||||||
|
redirectUserToLogin()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (refreshSessionRes.userId) {
|
||||||
|
userId = refreshSessionRes.userId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get user info by ID
|
||||||
|
const getByIdRes = await usersService.getById(userId)
|
||||||
|
|
||||||
|
if (getByIdRes.error) {
|
||||||
|
if (isVisitor && props.match.params.id) {
|
||||||
|
setToastMessage('Usuário não existe!')
|
||||||
|
setShowToast(true)
|
||||||
|
history.push({ pathname: '/home' })
|
||||||
|
} else {
|
||||||
|
setToastMessage(getByIdRes.error.errorMessage)
|
||||||
|
setShowToast(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if user is driver (if they have vans)
|
||||||
|
const userIsDriverRes = await usersService.checkIfUserIsDriver(userId)
|
||||||
|
|
||||||
|
// if (userIsDriverRes.error) {
|
||||||
|
// setToastColor('warning')
|
||||||
|
// setToastMessage(userIsDriverRes.error.errorMessage)
|
||||||
|
// setShowToast(true)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (!userIsDriverRes.error && userIsDriverRes.result !== undefined) {
|
||||||
|
setIsDriver(userIsDriverRes.result)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getByIdRes.userData) {
|
||||||
|
const userData = getByIdRes.userData
|
||||||
|
|
||||||
|
if (isMounted) {
|
||||||
|
setInputValues({
|
||||||
|
'id': userId,
|
||||||
|
'name': userData.name,
|
||||||
|
'lastname': userData.lastname,
|
||||||
|
'email': userData.email,
|
||||||
|
'phone_number': userData.phone_number,
|
||||||
|
'birth_date': userData.birth_date,
|
||||||
|
'bio': userData.bio,
|
||||||
|
'document_type': userData.document_type,
|
||||||
|
'document': userData.document
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!props.match.params.id) {
|
||||||
|
setIsVisitor(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!userData.document || !userData.phone_number) {
|
||||||
|
setIncompleteProfile(true)
|
||||||
|
|
||||||
|
let counter = 0
|
||||||
|
|
||||||
|
if (!userData.document) counter++
|
||||||
|
if (!userData.phone_number) counter++
|
||||||
|
|
||||||
|
setIncompleteProfileCounter(counter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let isMounted = true;
|
||||||
|
|
||||||
|
const userToken = LocalStorage.getToken()
|
||||||
|
|
||||||
|
if (!userToken) {
|
||||||
|
redirectUserToLogin()
|
||||||
|
}
|
||||||
|
|
||||||
|
loadUserData()
|
||||||
|
|
||||||
|
return () => { isMounted = false };
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IonPage>
|
||||||
|
<IonHeader>
|
||||||
|
<IonToolbar>
|
||||||
|
<IonTitle>Seu perfil</IonTitle>
|
||||||
|
<IonButtons slot="start">
|
||||||
|
<IonBackButton defaultHref="/home" />
|
||||||
|
</IonButtons>
|
||||||
|
</IonToolbar>
|
||||||
|
</IonHeader>
|
||||||
|
|
||||||
|
<IonContent>
|
||||||
|
<IonHeader collapse="condense">
|
||||||
|
<IonToolbar>
|
||||||
|
<IonTitle size="large">Seu perfil</IonTitle>
|
||||||
|
</IonToolbar>
|
||||||
|
</IonHeader>
|
||||||
|
|
||||||
|
<IonCard>
|
||||||
|
<IonCardContent>
|
||||||
|
<img src="https://static.generated.photos/vue-static/home/feed/adult.png" alt="avatar" className='avatar' id='avatar'/>
|
||||||
|
{/* <img src="https://lastfm.freetls.fastly.net/i/u/avatar170s/faa68f71f3b2a48ca89228c2c2aa72d3" alt="avatar" className='avatar' id='avatar'/> */}
|
||||||
|
<IonCardHeader>
|
||||||
|
<IonCardTitle class="ion-text-center">{inputValues.name} {inputValues.lastname}</IonCardTitle>
|
||||||
|
</IonCardHeader>
|
||||||
|
|
||||||
|
<div id='profile-status'>
|
||||||
|
{ isDriver ?
|
||||||
|
<>
|
||||||
|
<IonChip>
|
||||||
|
<IonIcon icon={carOutline}></IonIcon>
|
||||||
|
<IonLabel color="primary">Motorista</IonLabel>
|
||||||
|
</IonChip>
|
||||||
|
</> : <></>
|
||||||
|
}
|
||||||
|
<IonChip>
|
||||||
|
<IonIcon icon={personOutline}></IonIcon>
|
||||||
|
<IonLabel color="primary">Passageiro</IonLabel>
|
||||||
|
</IonChip>
|
||||||
|
</div>
|
||||||
|
</IonCardContent>
|
||||||
|
</IonCard>
|
||||||
|
|
||||||
|
<IonCard>
|
||||||
|
<IonCardHeader>
|
||||||
|
<IonCardTitle>Biografia</IonCardTitle>
|
||||||
|
</IonCardHeader>
|
||||||
|
<IonCardContent>
|
||||||
|
{inputValues.bio ? inputValues.bio : 'Sem biografia.' }
|
||||||
|
</IonCardContent>
|
||||||
|
</IonCard>
|
||||||
|
|
||||||
|
<IonCard>
|
||||||
|
<IonCardHeader>
|
||||||
|
<IonCardTitle>Informações de contato</IonCardTitle>
|
||||||
|
</IonCardHeader>
|
||||||
|
<IonCardContent>
|
||||||
|
{ !inputValues.phone_number ?
|
||||||
|
<>Sem informações de contato.</>
|
||||||
|
: <>
|
||||||
|
{
|
||||||
|
inputValues.phone_number ?
|
||||||
|
<>
|
||||||
|
<IonChip>
|
||||||
|
<IonIcon icon={callOutline} />
|
||||||
|
<IonLabel>{inputValues.phone_number}</IonLabel>
|
||||||
|
</IonChip>
|
||||||
|
</> : <></>
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
</IonCardContent>
|
||||||
|
</IonCard>
|
||||||
|
|
||||||
|
{ !isVisitor ?
|
||||||
|
<IonList>
|
||||||
|
<IonListHeader>Configurações</IonListHeader>
|
||||||
|
<IonItem button onClick={() => history.push({ pathname: '/perfil/editar', state: { userData: inputValues } })}>
|
||||||
|
<IonIcon icon={createOutline} slot="start" />
|
||||||
|
<IonLabel>Editar perfil</IonLabel>
|
||||||
|
</IonItem>
|
||||||
|
|
||||||
|
{ incompleteProfile ?
|
||||||
|
<>
|
||||||
|
<IonItem button onClick={() => history.push({ pathname: '/perfil/completar', state: { userData: inputValues } })}>
|
||||||
|
<IonIcon icon={shieldCheckmarkOutline} slot="start" />
|
||||||
|
<IonLabel>Completar cadastro</IonLabel>
|
||||||
|
<IonBadge color="primary">{incompleteProfileCounter}</IonBadge>
|
||||||
|
</IonItem>
|
||||||
|
</>
|
||||||
|
: <></> }
|
||||||
|
|
||||||
|
<IonItem button onClick={() => history.push({ pathname: '/cadastro-van'})}>
|
||||||
|
<IonIcon icon={carOutline} slot="start" />
|
||||||
|
<IonLabel>Cadastrar Van</IonLabel>
|
||||||
|
</IonItem>
|
||||||
|
<IonItem button onClick={() => history.push({ pathname: '/minhas-vans'})}>
|
||||||
|
<IonIcon icon={carOutline} slot="start" />
|
||||||
|
<IonLabel>Minhas Vans</IonLabel>
|
||||||
|
</IonItem>
|
||||||
|
<IonItem>
|
||||||
|
<IonIcon icon={cardOutline} slot="start" />
|
||||||
|
<IonLabel>Pagamentos</IonLabel>
|
||||||
|
</IonItem>
|
||||||
|
<IonItem>
|
||||||
|
<IonIcon icon={starOutline} slot="start" />
|
||||||
|
<IonLabel>Avaliações</IonLabel>
|
||||||
|
</IonItem>
|
||||||
|
<IonItem button onClick={logoff}>
|
||||||
|
<IonIcon icon={exitOutline} slot="start" />
|
||||||
|
<IonLabel>Sair da conta</IonLabel>
|
||||||
|
</IonItem>
|
||||||
|
</IonList> : <></>
|
||||||
|
}
|
||||||
|
|
||||||
|
<IonToast
|
||||||
|
position="top"
|
||||||
|
color={toastColor}
|
||||||
|
isOpen={showToast}
|
||||||
|
onDidDismiss={() => setShowToast(false)}
|
||||||
|
message={toastMessage}
|
||||||
|
duration={2500}
|
||||||
|
/>
|
||||||
|
</IonContent>
|
||||||
|
</IonPage>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Perfil;
|
||||||
200
src/pages/PerfilEditar.tsx
Normal file
200
src/pages/PerfilEditar.tsx
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
import {
|
||||||
|
IonBackButton,
|
||||||
|
IonButtons,
|
||||||
|
IonContent,
|
||||||
|
IonFab,
|
||||||
|
IonFabButton,
|
||||||
|
IonGrid,
|
||||||
|
IonHeader,
|
||||||
|
IonIcon,
|
||||||
|
IonInput,
|
||||||
|
IonItem,
|
||||||
|
IonLabel,
|
||||||
|
IonPage,
|
||||||
|
IonTextarea,
|
||||||
|
IonTitle,
|
||||||
|
IonToast,
|
||||||
|
IonToolbar
|
||||||
|
} from "@ionic/react";
|
||||||
|
import React, { useEffect, useReducer, useState } from "react";
|
||||||
|
import { IonRow, IonCol } from "@ionic/react";
|
||||||
|
|
||||||
|
import './Perfil.css'
|
||||||
|
import { useHistory, useLocation } from "react-router";
|
||||||
|
import { saveOutline } from "ionicons/icons";
|
||||||
|
|
||||||
|
import isEqual from 'lodash.isequal';
|
||||||
|
|
||||||
|
import * as usersRoutes from '../services/api/users';
|
||||||
|
|
||||||
|
import './Cadastro/Cadastro.css'
|
||||||
|
import { Color } from "@ionic/react/node_modules/@ionic/core";
|
||||||
|
|
||||||
|
interface userData {
|
||||||
|
name: string;
|
||||||
|
lastname: string;
|
||||||
|
email: string;
|
||||||
|
birth_date: string;
|
||||||
|
bio: 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 [toastColor, setToastColor] = useState<Color>("primary");
|
||||||
|
|
||||||
|
const [userData, setUserData] = useState({
|
||||||
|
name: '',
|
||||||
|
lastname: '',
|
||||||
|
email: '',
|
||||||
|
birth_date: '',
|
||||||
|
bio: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const [inputValues, setInputValues] = useReducer(
|
||||||
|
(state: any, newState: any) => ({ ...state, ...newState }),
|
||||||
|
{
|
||||||
|
name: '',
|
||||||
|
lastname: '',
|
||||||
|
email: '',
|
||||||
|
birth_date: '',
|
||||||
|
bio: '',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!location.state) {
|
||||||
|
history.push({ pathname: '/perfil' })
|
||||||
|
}
|
||||||
|
|
||||||
|
let userData = location.state.userData
|
||||||
|
|
||||||
|
setUserData(location.state.userData)
|
||||||
|
setInputValues({
|
||||||
|
'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') {
|
||||||
|
setToastColor("danger")
|
||||||
|
setMessageToast(response.message);
|
||||||
|
setShowToast(true);
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
history.push({ pathname: '/perfil', state: {
|
||||||
|
redirectData: {
|
||||||
|
showToastMessage: true,
|
||||||
|
toastColor: "success",
|
||||||
|
toastMessage: response.message,
|
||||||
|
},
|
||||||
|
}})
|
||||||
|
}).catch((err) => {
|
||||||
|
setToastColor("danger")
|
||||||
|
setMessageToast(err);
|
||||||
|
setShowToast(true);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasChangedSinceInitialState = () => {
|
||||||
|
return isEqual(userData, inputValues)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IonPage>
|
||||||
|
<IonHeader>
|
||||||
|
<IonToolbar>
|
||||||
|
<IonTitle>Editar perfil</IonTitle>
|
||||||
|
<IonButtons slot="start">
|
||||||
|
<IonBackButton defaultHref="/perfil" />
|
||||||
|
</IonButtons>
|
||||||
|
</IonToolbar>
|
||||||
|
</IonHeader>
|
||||||
|
|
||||||
|
<IonContent>
|
||||||
|
<IonGrid>
|
||||||
|
<IonRow>
|
||||||
|
<IonCol size="12">
|
||||||
|
<div id='nome-sobrenome'>
|
||||||
|
<IonItem>
|
||||||
|
<IonLabel position="stacked"> Nome</IonLabel>
|
||||||
|
<IonInput
|
||||||
|
type="text"
|
||||||
|
value={inputValues.name}
|
||||||
|
onIonChange={(e) => setInputValues({'name': e.detail.value!})}
|
||||||
|
></IonInput>
|
||||||
|
</IonItem>
|
||||||
|
|
||||||
|
<IonItem>
|
||||||
|
<IonLabel position="stacked"> Sobrenome</IonLabel>
|
||||||
|
<IonInput
|
||||||
|
type="text"
|
||||||
|
value={inputValues.lastname}
|
||||||
|
onIonChange={(e) => setInputValues({'lastname': e.detail.value!})}
|
||||||
|
></IonInput>
|
||||||
|
</IonItem>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<IonItem>
|
||||||
|
<IonLabel position="stacked"> Email</IonLabel>
|
||||||
|
<IonInput
|
||||||
|
type="email"
|
||||||
|
value={inputValues.email}
|
||||||
|
onIonChange={(e) => setInputValues({'email': e.detail.value!})}
|
||||||
|
></IonInput>
|
||||||
|
</IonItem>
|
||||||
|
|
||||||
|
<IonItem>
|
||||||
|
<IonLabel position='stacked'>Data de nascimento</IonLabel>
|
||||||
|
<IonInput
|
||||||
|
type='date'
|
||||||
|
value={inputValues.birth_date}
|
||||||
|
onIonInput={(e: any) => setInputValues({'birth_date': e.detail.value!})}
|
||||||
|
>
|
||||||
|
</IonInput>
|
||||||
|
</IonItem>
|
||||||
|
|
||||||
|
<IonItem>
|
||||||
|
<IonLabel position="stacked"> Biografia</IonLabel>
|
||||||
|
<IonTextarea
|
||||||
|
value={inputValues.bio}
|
||||||
|
onIonChange={(e) => setInputValues({'bio': e.detail.value!})}
|
||||||
|
></IonTextarea>
|
||||||
|
</IonItem>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
</IonGrid>
|
||||||
|
|
||||||
|
<IonFab vertical="bottom" horizontal="end" slot="fixed">
|
||||||
|
<IonFabButton disabled={hasChangedSinceInitialState()} onClick={handleUpdateUserData}>
|
||||||
|
<IonIcon icon={saveOutline} />
|
||||||
|
</IonFabButton>
|
||||||
|
</IonFab>
|
||||||
|
|
||||||
|
<IonToast
|
||||||
|
color={toastColor}
|
||||||
|
isOpen={showToast}
|
||||||
|
onDidDismiss={() => setShowToast(false)}
|
||||||
|
message={messageToast}
|
||||||
|
duration={2500}
|
||||||
|
/>
|
||||||
|
</IonContent>
|
||||||
|
</IonPage>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PerfilEditar;
|
||||||
78
src/pages/Transportes/Transportes.css
Normal file
78
src/pages/Transportes/Transportes.css
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
.header-page{
|
||||||
|
background-color: var(--ion-item-background);
|
||||||
|
border-bottom: 1px solid var(--ion-color-step-150, #dadada);
|
||||||
|
height: 3.5rem;
|
||||||
|
display: flex;
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-return{
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.span-info-back{
|
||||||
|
background-color: var(--ion-color-step-150, #dadada);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
padding: 0.5rem;
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.address-from-to{
|
||||||
|
/* display: block; */
|
||||||
|
margin-top: -0.2rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* .address-from-to span{
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
width: 1rem!important;
|
||||||
|
} */
|
||||||
|
|
||||||
|
.address-from-to ion-icon{
|
||||||
|
margin-inline: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.address-from-to small{
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-tabs{
|
||||||
|
background-color: var(--ion-color-step-150,#dadada);
|
||||||
|
height: 5.5rem;
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-tabs h5{
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-tabs .card-transporte{
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-filter-modal{
|
||||||
|
margin: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-filter-modal{
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-criar-alerta{
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
margin: 1rem 0 5.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.msg-not-found{
|
||||||
|
margin: 1.5rem;
|
||||||
|
}
|
||||||
222
src/pages/Transportes/Transportes.tsx
Normal file
222
src/pages/Transportes/Transportes.tsx
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
import {
|
||||||
|
IonContent,
|
||||||
|
IonPage,
|
||||||
|
IonFab,
|
||||||
|
IonFabButton,
|
||||||
|
IonIcon,
|
||||||
|
IonCard,
|
||||||
|
IonInput,
|
||||||
|
IonRow,
|
||||||
|
IonCol,
|
||||||
|
IonCardContent,
|
||||||
|
IonButton,
|
||||||
|
IonHeader,
|
||||||
|
IonToolbar,
|
||||||
|
IonButtons,
|
||||||
|
IonBackButton,
|
||||||
|
IonTabs,
|
||||||
|
IonTabBar,
|
||||||
|
IonTabButton,
|
||||||
|
IonLabel,
|
||||||
|
IonBadge,
|
||||||
|
IonRouterOutlet,
|
||||||
|
IonSlides,
|
||||||
|
IonSlide,
|
||||||
|
IonModal,
|
||||||
|
IonList,
|
||||||
|
IonRadioGroup,
|
||||||
|
IonListHeader,
|
||||||
|
IonItem,
|
||||||
|
IonRadio,
|
||||||
|
IonCheckbox,
|
||||||
|
IonFooter,
|
||||||
|
IonToast,
|
||||||
|
} from "@ionic/react";
|
||||||
|
import {
|
||||||
|
arrowBack,
|
||||||
|
arrowBackOutline,
|
||||||
|
arrowForwardOutline,
|
||||||
|
chevronBackOutline,
|
||||||
|
chevronForwardOutline,
|
||||||
|
closeOutline,
|
||||||
|
locateOutline,
|
||||||
|
locationOutline,
|
||||||
|
timeOutline,
|
||||||
|
} from "ionicons/icons";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useHistory, useLocation } from "react-router";
|
||||||
|
import { getTransportes } from "../../services/transportes";
|
||||||
|
import { createUserSearch } from "../../services/api/users";
|
||||||
|
import "./Transportes.css";
|
||||||
|
|
||||||
|
interface InfoBusca {
|
||||||
|
addressFrom: any;
|
||||||
|
addressTo: any;
|
||||||
|
coordinatesFrom: any;
|
||||||
|
coordinatesTo: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Transportes: React.FC = () => {
|
||||||
|
const history = useHistory();
|
||||||
|
const location = useLocation();
|
||||||
|
const props = location.state as InfoBusca;
|
||||||
|
const [transportes, setTransportes] = useState([]);
|
||||||
|
const [showModalFilters, setShowModalFilters] = useState(false);
|
||||||
|
const [showToast, setShowToast] = useState(false);
|
||||||
|
const [messageToast, setMessageToast ] = useState('');
|
||||||
|
const [toastColor, setToastColor] = useState('success');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (props) {
|
||||||
|
buscaTransportes();
|
||||||
|
}
|
||||||
|
}, [props]);
|
||||||
|
|
||||||
|
async function buscaTransportes() {
|
||||||
|
let data = (await getTransportes(props)) as any;
|
||||||
|
setTransportes(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function criaAlerta(){
|
||||||
|
createUserSearch(props.coordinatesFrom.lat, props.coordinatesFrom.lng, props.addressTo.label).then(() => {
|
||||||
|
setMessageToast('Alerta criado com sucesso!');
|
||||||
|
setShowToast(true);
|
||||||
|
}).catch((err:any) => {
|
||||||
|
setMessageToast('Não foi possível criar o alerta!');
|
||||||
|
setToastColor('danger');
|
||||||
|
setShowToast(true);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IonPage>
|
||||||
|
<IonHeader>
|
||||||
|
<div className="header-page">
|
||||||
|
{/* <IonButtons slot="start">
|
||||||
|
<IonBackButton text={'aaaa'} icon={arrowBack} defaultHref='buscar-transporte' />
|
||||||
|
</IonButtons> */}
|
||||||
|
<span className="span-info-back" onClick={history.goBack}>
|
||||||
|
<IonIcon className="icon-return" icon={chevronBackOutline} />
|
||||||
|
<div className="address-from-to">
|
||||||
|
<span>{props.addressFrom.label}</span>
|
||||||
|
<IonIcon icon={arrowForwardOutline} />
|
||||||
|
<span>{props.addressTo.label}</span>
|
||||||
|
<small>Hoje</small>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</IonHeader>
|
||||||
|
<IonContent fullscreen>
|
||||||
|
{transportes && transportes.length > 0? (
|
||||||
|
<div className="header-tabs">
|
||||||
|
<IonSlides>
|
||||||
|
<IonSlide>
|
||||||
|
<h5>Mais barata</h5>
|
||||||
|
<IonCard className="card-transporte">
|
||||||
|
<IonCardContent>Seu João</IonCardContent>
|
||||||
|
</IonCard>
|
||||||
|
</IonSlide>
|
||||||
|
<IonSlide>
|
||||||
|
<h5>Melhor avaliação</h5>
|
||||||
|
<IonCard className="card-transporte">
|
||||||
|
<IonCardContent>Seu Zé</IonCardContent>
|
||||||
|
</IonCard>
|
||||||
|
</IonSlide>
|
||||||
|
</IonSlides>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
:
|
||||||
|
(<h1 className="msg-not-found">Não foi encontrado nenhum transporte que atenda essa rota.</h1>)}
|
||||||
|
{transportes &&
|
||||||
|
transportes.map((record: any, index: any) => {
|
||||||
|
return (
|
||||||
|
<IonCard className="card-transporte" key={index}>
|
||||||
|
<IonCardContent>
|
||||||
|
<h1>Motorista: {record.motorista}</h1>
|
||||||
|
<div>Avaliação: {record.avaliacao}</div>
|
||||||
|
<div>Valor: {record.valor}</div>
|
||||||
|
<div>Lugares disponíveis: {record.lugares}</div>
|
||||||
|
</IonCardContent>
|
||||||
|
</IonCard>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
|
<div className="button-criar-alerta">
|
||||||
|
<IonButton onClick={() => criaAlerta()}>Criar Alerta</IonButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<IonFab
|
||||||
|
onClick={() => setShowModalFilters(true)}
|
||||||
|
vertical="bottom"
|
||||||
|
horizontal="center"
|
||||||
|
slot="fixed"
|
||||||
|
>
|
||||||
|
<IonFabButton>Filtros</IonFabButton>
|
||||||
|
</IonFab>
|
||||||
|
<IonModal isOpen={showModalFilters}>
|
||||||
|
<IonToolbar>
|
||||||
|
<div className="header-filter-modal">
|
||||||
|
<IonIcon
|
||||||
|
size="large"
|
||||||
|
icon={closeOutline}
|
||||||
|
onClick={() => setShowModalFilters(false)}
|
||||||
|
/>
|
||||||
|
<h4>
|
||||||
|
<b>Limpar</b>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
</IonToolbar>
|
||||||
|
<IonContent>
|
||||||
|
<div className="content-filter-modal">
|
||||||
|
<h1>Filtrar</h1>
|
||||||
|
<h3>Ordernar por</h3>
|
||||||
|
<IonRadioGroup>
|
||||||
|
<IonItem>
|
||||||
|
<IonLabel>Menor preço</IonLabel>
|
||||||
|
<IonRadio value="menor_preco" />
|
||||||
|
</IonItem>
|
||||||
|
|
||||||
|
<IonItem>
|
||||||
|
<IonLabel>Avaliação</IonLabel>
|
||||||
|
<IonRadio value="avaliacao" />
|
||||||
|
</IonItem>
|
||||||
|
|
||||||
|
<IonItem>
|
||||||
|
<IonLabel>Lugares disponíveis</IonLabel>
|
||||||
|
<IonRadio value="lugares_disponiveis" />
|
||||||
|
</IonItem>
|
||||||
|
</IonRadioGroup>
|
||||||
|
<h3>Preferências</h3>
|
||||||
|
<IonItem>
|
||||||
|
<IonLabel>Vaga avulsa</IonLabel>
|
||||||
|
<IonCheckbox value="vaga_avulsa" />
|
||||||
|
</IonItem>
|
||||||
|
<IonItem>
|
||||||
|
<IonLabel>Ar condicionado</IonLabel>
|
||||||
|
<IonCheckbox value="ar_condicionado" />
|
||||||
|
</IonItem>
|
||||||
|
</div>
|
||||||
|
</IonContent>
|
||||||
|
<IonFooter>
|
||||||
|
<IonButton
|
||||||
|
expand="block"
|
||||||
|
onClick={() => setShowModalFilters(false)}
|
||||||
|
>
|
||||||
|
Aplicar Filtros
|
||||||
|
</IonButton>
|
||||||
|
</IonFooter>
|
||||||
|
</IonModal>
|
||||||
|
<IonToast
|
||||||
|
// cssClass={"toast-notification"}
|
||||||
|
color={toastColor}
|
||||||
|
isOpen={showToast}
|
||||||
|
onDidDismiss={() => setShowToast(false)}
|
||||||
|
message={messageToast}
|
||||||
|
duration={2500}
|
||||||
|
/>
|
||||||
|
</IonContent>
|
||||||
|
</IonPage>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Transportes;
|
||||||
@@ -1,162 +0,0 @@
|
|||||||
import {
|
|
||||||
IonContent,
|
|
||||||
IonHeader,
|
|
||||||
IonPage,
|
|
||||||
IonTitle,
|
|
||||||
IonToolbar,
|
|
||||||
IonButtons,
|
|
||||||
} from "@ionic/react";
|
|
||||||
import React, { useState } from "react";
|
|
||||||
import axios from "axios";
|
|
||||||
import { IonGrid, IonRow, IonCol } from "@ionic/react";
|
|
||||||
import { personCircle } from "ionicons/icons";
|
|
||||||
import { useHistory } from "react-router-dom";
|
|
||||||
import {
|
|
||||||
IonItem,
|
|
||||||
IonLabel,
|
|
||||||
IonInput,
|
|
||||||
IonButton,
|
|
||||||
IonIcon,
|
|
||||||
IonAlert,
|
|
||||||
} from "@ionic/react";
|
|
||||||
|
|
||||||
function validateEmail(email: string) {
|
|
||||||
const re =
|
|
||||||
/^((?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\]))$/;
|
|
||||||
return re.test(String(email).toLowerCase());
|
|
||||||
}
|
|
||||||
|
|
||||||
const Page: React.FC = () => {
|
|
||||||
const history = useHistory();
|
|
||||||
const [email, setEmail] = useState<string>("matheusalb3213@gmail.com");
|
|
||||||
const [password, setPassword] = useState<string>("1234");
|
|
||||||
const [isError, setIsError] = useState<boolean>(false);
|
|
||||||
const [message, setMessage] = useState<string>("");
|
|
||||||
|
|
||||||
const handleLogin = () => {
|
|
||||||
// validação de inputs
|
|
||||||
if (!email) {
|
|
||||||
setMessage("Por favor, informe um e-mail válido");
|
|
||||||
setIsError(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (validateEmail(email) === false) {
|
|
||||||
setMessage("E-mail inválido");
|
|
||||||
setIsError(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!password || password.length < 6) {
|
|
||||||
setMessage("Por favor, digite a sua senha");
|
|
||||||
setIsError(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const loginData = {
|
|
||||||
email: email,
|
|
||||||
password: password,
|
|
||||||
};
|
|
||||||
|
|
||||||
const api = axios.create({
|
|
||||||
baseURL: `https://625dc16c4c36c7535779792c.mockapi.io/api/v1`,
|
|
||||||
});
|
|
||||||
|
|
||||||
api
|
|
||||||
// .post("/login", loginData)
|
|
||||||
.get("/users/2")
|
|
||||||
.then((res) => {
|
|
||||||
// login bem-sucedido
|
|
||||||
history.push("/dashboard/" + email);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
setMessage("Falha na autenticação! Por favor, crie uma conta");
|
|
||||||
setIsError(true);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<IonPage>
|
|
||||||
<IonHeader>
|
|
||||||
<IonToolbar>
|
|
||||||
<IonTitle>Login</IonTitle>
|
|
||||||
</IonToolbar>
|
|
||||||
</IonHeader>
|
|
||||||
|
|
||||||
<IonRow>
|
|
||||||
<IonCol>
|
|
||||||
<IonIcon
|
|
||||||
style={{ fontSize: "70px", color: "#0040ff" }}
|
|
||||||
icon={personCircle}
|
|
||||||
/>
|
|
||||||
</IonCol>
|
|
||||||
</IonRow>
|
|
||||||
|
|
||||||
<IonContent fullscreen>
|
|
||||||
<IonHeader collapse="condense">
|
|
||||||
<IonToolbar>
|
|
||||||
<IonTitle size="large">Login</IonTitle>
|
|
||||||
</IonToolbar>
|
|
||||||
</IonHeader>
|
|
||||||
|
|
||||||
<IonGrid>
|
|
||||||
<IonRow>
|
|
||||||
<IonCol>
|
|
||||||
<IonAlert
|
|
||||||
isOpen={isError}
|
|
||||||
onDidDismiss={() => setIsError(false)}
|
|
||||||
cssClass="my-custom-class"
|
|
||||||
header={"Error!"}
|
|
||||||
message={message}
|
|
||||||
buttons={["Dismiss"]}
|
|
||||||
/>
|
|
||||||
</IonCol>
|
|
||||||
</IonRow>
|
|
||||||
|
|
||||||
<IonRow>
|
|
||||||
<IonCol>
|
|
||||||
<IonItem>
|
|
||||||
<IonLabel position="floating"> Email</IonLabel>
|
|
||||||
<IonInput
|
|
||||||
type="email"
|
|
||||||
value={email}
|
|
||||||
onIonChange={(e) => setEmail(e.detail.value!)}
|
|
||||||
></IonInput>
|
|
||||||
</IonItem>
|
|
||||||
</IonCol>
|
|
||||||
</IonRow>
|
|
||||||
|
|
||||||
<IonRow>
|
|
||||||
<IonCol>
|
|
||||||
<IonItem>
|
|
||||||
<IonLabel position="floating"> Senha</IonLabel>
|
|
||||||
<IonInput
|
|
||||||
type="password"
|
|
||||||
value={password}
|
|
||||||
onIonChange={(e) => setPassword(e.detail.value!)}
|
|
||||||
></IonInput>
|
|
||||||
</IonItem>
|
|
||||||
</IonCol>
|
|
||||||
</IonRow>
|
|
||||||
|
|
||||||
<IonRow>
|
|
||||||
<IonCol>
|
|
||||||
<p style={{ fontSize: "small" }}>
|
|
||||||
Clicando no botão de "LOGIN", você concorda com a nossa{" "}
|
|
||||||
<a href="#">política de termos e serviços</a>
|
|
||||||
</p>
|
|
||||||
<IonButton expand="block" onClick={handleLogin}>
|
|
||||||
Login
|
|
||||||
</IonButton>
|
|
||||||
<p style={{ fontSize: "medium" }}>
|
|
||||||
Ainda não possui uma conta? <a href="cadastro">Cadastre-se aqui!</a>
|
|
||||||
</p>
|
|
||||||
</IonCol>
|
|
||||||
</IonRow>
|
|
||||||
</IonGrid>
|
|
||||||
</IonContent>
|
|
||||||
</IonPage>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Page;
|
|
||||||
18
src/services/api-client.service.ts
Normal file
18
src/services/api-client.service.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
export class ApiClient{
|
||||||
|
private static api = axios.create({
|
||||||
|
baseURL: "http://localhost:8080"
|
||||||
|
});
|
||||||
|
|
||||||
|
public static async doPost (url: string, body: any): Promise<any> {
|
||||||
|
return await this.api
|
||||||
|
.post(url, body)
|
||||||
|
.then(res => {
|
||||||
|
console.log(res.data);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import apiConfig from '../config/api.config';
|
import apiConfig from '../../config/api.config';
|
||||||
|
|
||||||
const instance = axios.create({
|
const instance = axios.create({
|
||||||
baseURL: apiConfig.getBaseUrl(),
|
baseURL: apiConfig.getBaseUrl(),
|
||||||
25
src/services/api/cars.ts
Normal file
25
src/services/api/cars.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import instance from './api';
|
||||||
|
|
||||||
|
import carsRoutes from '../../constants/routes/carsRoutes';
|
||||||
|
import { AxiosRequestHeaders } from 'axios';
|
||||||
|
import LocalStorage from '../../LocalStorage';
|
||||||
|
|
||||||
|
let token: string;
|
||||||
|
let header: AxiosRequestHeaders;
|
||||||
|
|
||||||
|
function updateHeader() {
|
||||||
|
token = LocalStorage.getToken();
|
||||||
|
|
||||||
|
header = {
|
||||||
|
"Accept": 'application/json',
|
||||||
|
"Content-Type": 'application/json',
|
||||||
|
"Authorization": 'Bearer ' + token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export async function list() {
|
||||||
|
updateHeader();
|
||||||
|
|
||||||
|
const response = await instance.get(carsRoutes.list.url, { headers: header });
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
33
src/services/api/session.ts
Normal file
33
src/services/api/session.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import instance from './api';
|
||||||
|
import sessionRoutes from '../../constants/routes/sessionRoutes';
|
||||||
|
import LocalStorage from '../../LocalStorage';
|
||||||
|
import { AxiosRequestHeaders } from 'axios';
|
||||||
|
|
||||||
|
let token: string | null;
|
||||||
|
let header: AxiosRequestHeaders;
|
||||||
|
|
||||||
|
interface createData {
|
||||||
|
login: string,
|
||||||
|
password: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateHeader() {
|
||||||
|
token = LocalStorage.getToken();
|
||||||
|
header = {
|
||||||
|
"Accept": 'application/json',
|
||||||
|
"Content-Type": 'application/json',
|
||||||
|
"Authorization": 'Bearer ' + token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function create(data: createData) {
|
||||||
|
const response = await instance.post(sessionRoutes.create.url, data);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function refresh() {
|
||||||
|
updateHeader();
|
||||||
|
|
||||||
|
let response = await instance.post(sessionRoutes.refresh.url, { token }, { headers: header });
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
109
src/services/api/users.ts
Normal file
109
src/services/api/users.ts
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
import instance from './api';
|
||||||
|
// import LocalStorage from '../LocalStorage';
|
||||||
|
|
||||||
|
import userRoutes from '../../constants/routes/usersRoutes';
|
||||||
|
import { AxiosRequestHeaders } from 'axios';
|
||||||
|
import LocalStorage from '../../LocalStorage';
|
||||||
|
import { setStore } from '../../store/RecordsStore';
|
||||||
|
|
||||||
|
let token: string;
|
||||||
|
let header: AxiosRequestHeaders;
|
||||||
|
|
||||||
|
function updateHeader() {
|
||||||
|
token = LocalStorage.getToken();
|
||||||
|
|
||||||
|
header = {
|
||||||
|
"Accept": 'application/json',
|
||||||
|
"Content-Type": 'application/json',
|
||||||
|
"Authorization": 'Bearer ' + token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CadastroResponse {
|
||||||
|
message?: string;
|
||||||
|
|
||||||
|
token?: {
|
||||||
|
token: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CadastroRequest {
|
||||||
|
name: string;
|
||||||
|
lastname: string;
|
||||||
|
email: string;
|
||||||
|
birth_date: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateUserRequest {
|
||||||
|
name?: string;
|
||||||
|
email?: string;
|
||||||
|
bio?: string;
|
||||||
|
document_type?: string;
|
||||||
|
document?: string;
|
||||||
|
phone_number?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// export async function get(cpf) {
|
||||||
|
// updateHeader();
|
||||||
|
|
||||||
|
// const response = await instance.get(userRoutes.get.url + `/${cpf}`, { headers: header });
|
||||||
|
// return response.data;
|
||||||
|
// }
|
||||||
|
|
||||||
|
export async function create(CadastroRequest: any) {
|
||||||
|
updateHeader();
|
||||||
|
|
||||||
|
const response = await instance.post(userRoutes.create.url, CadastroRequest);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getById(userId: string) {
|
||||||
|
updateHeader();
|
||||||
|
|
||||||
|
const response = await instance.get(userRoutes.get.url + `/${userId}`, { headers: header });
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function update(userData: UpdateUserRequest) {
|
||||||
|
updateHeader();
|
||||||
|
|
||||||
|
const response = await instance.patch(userRoutes.update.url, userData, { headers: header });
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function checkIfUserIsDriver(id_user: string) {
|
||||||
|
updateHeader();
|
||||||
|
|
||||||
|
const response = await instance.get(userRoutes.checkIfUserIsDriver.url + `/${id_user}`, { headers: header });
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO, continuar
|
||||||
|
export async function getSocialInfo(userId: string) {
|
||||||
|
updateHeader();
|
||||||
|
|
||||||
|
const response = await instance.get(userRoutes.getSocialInfo.url + `/${userId}`, { headers: header });
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getUsersSearching(currentPoint: any) {
|
||||||
|
// Replace lat/long with values from get current location.
|
||||||
|
// Allow choosing of radius?
|
||||||
|
// Offset could = amount loaded in an infinite scroll?
|
||||||
|
var latitude = currentPoint.latitude, longitude = currentPoint.longitude, radius = 3000, offset = 0;
|
||||||
|
// const response = await fetch(`http://localhost:4000/get-records?latitude=${ latitude }&longitude=${ longitude }&radius=${ radius }&offset=${ offset }`);
|
||||||
|
const response = await instance.post(`${userRoutes.getUsersSearching.url}`, currentPoint)
|
||||||
|
// const data = await response.json();
|
||||||
|
console.log(response.data)
|
||||||
|
setStore(response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createUserSearch(latitude_from: any, longitude_from: any, addres_to: any) {
|
||||||
|
const response = await instance.post(`${userRoutes.createUserSearch.url}`, { latitude_from, longitude_from, addres_to });
|
||||||
|
|
||||||
|
console.log(response)
|
||||||
|
setStore(response);
|
||||||
|
}
|
||||||
73
src/services/api/vans.ts
Normal file
73
src/services/api/vans.ts
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import instance from "./api";
|
||||||
|
|
||||||
|
import vansRoutes from "../../constants/routes/vansRoutes";
|
||||||
|
import { AxiosRequestHeaders } from "axios";
|
||||||
|
import LocalStorage from "../../LocalStorage";
|
||||||
|
|
||||||
|
let token: string;
|
||||||
|
let header: AxiosRequestHeaders;
|
||||||
|
|
||||||
|
function updateHeader() {
|
||||||
|
token = LocalStorage.getToken();
|
||||||
|
|
||||||
|
header = {
|
||||||
|
Accept: "application/json",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Authorization: "Bearer " + token,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getByPlate(vanId: string) {
|
||||||
|
updateHeader();
|
||||||
|
|
||||||
|
const response = await instance.get(vansRoutes.getByPlate.url + `/${vanId}`, {
|
||||||
|
headers: header,
|
||||||
|
});
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getByUserId(userId: string) {
|
||||||
|
updateHeader();
|
||||||
|
|
||||||
|
const response = await instance.get(vansRoutes.getByUserId.url + `/${userId}`, {
|
||||||
|
headers: header,
|
||||||
|
});
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CreateVanBody {
|
||||||
|
plate: string;
|
||||||
|
brand: string;
|
||||||
|
model: string;
|
||||||
|
seats_number: string;
|
||||||
|
locator_name: string;
|
||||||
|
locator_address: string;
|
||||||
|
locator_complement: string;
|
||||||
|
locator_city: string;
|
||||||
|
locator_state: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function create(CreateVanBody: CreateVanBody) {
|
||||||
|
updateHeader();
|
||||||
|
|
||||||
|
const response = await instance.post(vansRoutes.create.url, CreateVanBody, { headers: header });
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UpdateVanBody {
|
||||||
|
brand?: string;
|
||||||
|
model?: string;
|
||||||
|
seats_number?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function update(vanData: UpdateVanBody) {
|
||||||
|
updateHeader();
|
||||||
|
|
||||||
|
const response = await instance.patch(vansRoutes.update.url, vanData, {
|
||||||
|
headers: header,
|
||||||
|
});
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
51
src/services/functions/carsService.ts
Normal file
51
src/services/functions/carsService.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import * as carsRoutes from "../api/cars";
|
||||||
|
|
||||||
|
interface getAllCarModelsReturn {
|
||||||
|
data?: {
|
||||||
|
id_model: string;
|
||||||
|
name: string;
|
||||||
|
}[];
|
||||||
|
|
||||||
|
error?: {
|
||||||
|
errorMessage: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface getAllCarModelsRes {
|
||||||
|
status?: string;
|
||||||
|
|
||||||
|
message: string
|
||||||
|
|
||||||
|
data?: {
|
||||||
|
id_model: string;
|
||||||
|
name: string;
|
||||||
|
}[];
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAllCarModels = async (): Promise<getAllCarModelsReturn> => {
|
||||||
|
try {
|
||||||
|
let res: getAllCarModelsRes = await carsRoutes.list();
|
||||||
|
|
||||||
|
if (res.status === "error") {
|
||||||
|
return {
|
||||||
|
error: {
|
||||||
|
errorMessage: res.message,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: res.data,
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
return {
|
||||||
|
error: {
|
||||||
|
errorMessage: "Por favor, autentique-se.",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default { getAllCarModels };
|
||||||
46
src/services/functions/sessionsService.ts
Normal file
46
src/services/functions/sessionsService.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import * as sessionRoutes from "../api/session";
|
||||||
|
|
||||||
|
interface refreshSessionReturn {
|
||||||
|
userId?: string;
|
||||||
|
error?: boolean;
|
||||||
|
errorMessage?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface refreshSessionResponse {
|
||||||
|
status?: string;
|
||||||
|
message?: string;
|
||||||
|
userId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const refreshSession = async (): Promise<refreshSessionReturn> => {
|
||||||
|
try {
|
||||||
|
let res: refreshSessionResponse = await sessionRoutes.refresh()
|
||||||
|
|
||||||
|
if (res.status === "error") {
|
||||||
|
return {
|
||||||
|
error: true,
|
||||||
|
errorMessage: res.message,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
userId: res.userId,
|
||||||
|
};
|
||||||
|
} catch(err) {
|
||||||
|
return {
|
||||||
|
error: true,
|
||||||
|
errorMessage: "Por favor, autentique-se.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// catch (err: any) {
|
||||||
|
// if (err.response) {
|
||||||
|
// // The client was given an error response (5xx, 4xx)
|
||||||
|
// } else if (err.request) {
|
||||||
|
// // The client never received a response, and the request was never left
|
||||||
|
// } else {
|
||||||
|
// // Anything else
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
|
||||||
|
export default { refreshSession }
|
||||||
135
src/services/functions/usersService.ts
Normal file
135
src/services/functions/usersService.ts
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
import * as usersRoutes from "../api/users";
|
||||||
|
|
||||||
|
interface getByIdReturn {
|
||||||
|
userData?: {
|
||||||
|
name: string;
|
||||||
|
lastname: string;
|
||||||
|
email: string;
|
||||||
|
phone_number: string;
|
||||||
|
birth_date: string;
|
||||||
|
bio: string;
|
||||||
|
document_type: string;
|
||||||
|
document: string;
|
||||||
|
},
|
||||||
|
error?: {
|
||||||
|
errorMessage: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface getByIdRes {
|
||||||
|
status: string;
|
||||||
|
message: string;
|
||||||
|
userId?: string;
|
||||||
|
data: {
|
||||||
|
name: string;
|
||||||
|
lastname: string;
|
||||||
|
email: string;
|
||||||
|
phone_number: string;
|
||||||
|
birth_date: string;
|
||||||
|
bio: string;
|
||||||
|
document_type: string;
|
||||||
|
document: string;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const getById = async (userId: string): Promise<getByIdReturn> => {
|
||||||
|
try {
|
||||||
|
let res: getByIdRes = await usersRoutes.getById(userId)
|
||||||
|
|
||||||
|
if (res.status === "error") {
|
||||||
|
return {
|
||||||
|
error: {
|
||||||
|
errorMessage: res.message,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
userData: res.data,
|
||||||
|
};
|
||||||
|
} catch(err) {
|
||||||
|
return {
|
||||||
|
error: {
|
||||||
|
errorMessage: "Por favor, autentique-se.",
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
interface getByIdReturn {
|
||||||
|
data?: {
|
||||||
|
phone: '',
|
||||||
|
whatsapp: '',
|
||||||
|
facebook: '',
|
||||||
|
telegram: '',
|
||||||
|
},
|
||||||
|
error?: {
|
||||||
|
errorMessage: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getUserSocialInfo = async (userId: string): Promise<getByIdReturn> => {
|
||||||
|
try {
|
||||||
|
let res: getByIdRes = await usersRoutes.getSocialInfo(userId)
|
||||||
|
|
||||||
|
if (res.status === "error") {
|
||||||
|
return {
|
||||||
|
error: {
|
||||||
|
errorMessage: res.message,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
userData: res.data,
|
||||||
|
};
|
||||||
|
} catch(err) {
|
||||||
|
return {
|
||||||
|
error: {
|
||||||
|
errorMessage: "Por favor, autentique-se.",
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
interface checkIfUserIsDriverReturn {
|
||||||
|
result?: boolean;
|
||||||
|
error?: {
|
||||||
|
errorMessage: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface checkIfUserIsDriverResponse {
|
||||||
|
status: string;
|
||||||
|
message: string;
|
||||||
|
result?: boolean;
|
||||||
|
error?: {
|
||||||
|
errorMessage: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkIfUserIsDriver = async (id_user: string): Promise<checkIfUserIsDriverReturn> => {
|
||||||
|
try {
|
||||||
|
let res: checkIfUserIsDriverResponse = await usersRoutes.checkIfUserIsDriver(id_user)
|
||||||
|
|
||||||
|
if (res.status === "error") {
|
||||||
|
return {
|
||||||
|
error: {
|
||||||
|
errorMessage: res.message,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
result: res.result,
|
||||||
|
};
|
||||||
|
} catch(err) {
|
||||||
|
return {
|
||||||
|
error: {
|
||||||
|
errorMessage: "Por favor, autentique-se.",
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default { getById, getUserSocialInfo, checkIfUserIsDriver }
|
||||||
34
src/services/transportes.ts
Normal file
34
src/services/transportes.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import instance from "../services/api/api";
|
||||||
|
import { setStore } from "../store/RecordsStore";
|
||||||
|
// import LocalStorage from '../LocalStorage';
|
||||||
|
|
||||||
|
// let token:string;
|
||||||
|
let header: string;
|
||||||
|
|
||||||
|
function updateHeader() {
|
||||||
|
// token = LocalStorage.getToken();
|
||||||
|
header = `{
|
||||||
|
"Accept": 'application/json',
|
||||||
|
"Content-Type": 'application/json',
|
||||||
|
"Authorization": 'Bearer ' + token
|
||||||
|
}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CoordinatesRequest {
|
||||||
|
coordinatesFrom:{
|
||||||
|
lat: number,
|
||||||
|
lng: number
|
||||||
|
},
|
||||||
|
coordinatesTo:{
|
||||||
|
lat: number,
|
||||||
|
lng: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getTransportes(request: CoordinatesRequest) {
|
||||||
|
updateHeader();
|
||||||
|
|
||||||
|
console.log(request)
|
||||||
|
const response = await instance.post("http://localhost:3333/transportes/", request);
|
||||||
|
return response.data as [];
|
||||||
|
}
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
import instance from '../services/api';
|
|
||||||
// import LocalStorage from '../LocalStorage';
|
|
||||||
|
|
||||||
let token:string;
|
|
||||||
let header:string;
|
|
||||||
|
|
||||||
function updateHeader() {
|
|
||||||
// token = LocalStorage.getToken();
|
|
||||||
header = `{
|
|
||||||
"Accept": 'application/json',
|
|
||||||
"Content-Type": 'application/json',
|
|
||||||
"Authorization": 'Bearer ' + token
|
|
||||||
}`
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CadastroResponse {
|
|
||||||
message?: string;
|
|
||||||
|
|
||||||
token?: {
|
|
||||||
token: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
error?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CadastroRequest {
|
|
||||||
name: string;
|
|
||||||
email: string;
|
|
||||||
birth_date: string;
|
|
||||||
password: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// export async function get(cpf) {
|
|
||||||
// updateHeader();
|
|
||||||
|
|
||||||
// const response = await instance.get(userRoutes.get.url + `/${cpf}`, { headers: header });
|
|
||||||
// return response.data;
|
|
||||||
// }
|
|
||||||
|
|
||||||
export async function create(CadastroRequest: any) {
|
|
||||||
updateHeader();
|
|
||||||
|
|
||||||
const response = await instance.post("http://localhost:3333/users/", CadastroRequest);
|
|
||||||
return response.data;
|
|
||||||
}
|
|
||||||
7
src/services/utils.ts
Normal file
7
src/services/utils.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import instance from '../services/api/api';
|
||||||
|
|
||||||
|
export async function autoCompleteAddress(address:string) {
|
||||||
|
|
||||||
|
const response = await instance.get(`https://api.geoapify.com/v1/geocode/autocomplete?text=${address}&apiKey=ee574aacff6f440a84378bbbf7e2f20d`);
|
||||||
|
return response.data.features;
|
||||||
|
}
|
||||||
25
src/services/validateCpf.ts
Normal file
25
src/services/validateCpf.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
export default function validateCpf(cpf: string): Boolean {
|
||||||
|
let soma = 0, resto;
|
||||||
|
|
||||||
|
if (cpf === "00000000000") return false;
|
||||||
|
|
||||||
|
for (let i = 1; i <= 9; i++)
|
||||||
|
soma = soma + parseInt(cpf.substring(i - 1, i)) * (11 - i);
|
||||||
|
|
||||||
|
resto = (soma * 10) % 11;
|
||||||
|
|
||||||
|
if (resto === 10 || resto === 11) resto = 0;
|
||||||
|
if (resto !== parseInt(cpf.substring(9, 10))) return false;
|
||||||
|
|
||||||
|
soma = 0;
|
||||||
|
for (let i = 1; i <= 10; i++)
|
||||||
|
soma = soma + parseInt(cpf.substring(i - 1, i)) * (12 - i);
|
||||||
|
|
||||||
|
resto = (soma * 10) % 11;
|
||||||
|
|
||||||
|
if (resto === 10 || resto === 11) resto = 0;
|
||||||
|
|
||||||
|
if (resto !== parseInt(cpf.substring(10, 11))) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
14
src/store/RecordsStore.js
Normal file
14
src/store/RecordsStore.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { Store } from 'pullstate';
|
||||||
|
|
||||||
|
const RecordsStore = new Store({
|
||||||
|
|
||||||
|
records: [],
|
||||||
|
center : []
|
||||||
|
});
|
||||||
|
|
||||||
|
export default RecordsStore;
|
||||||
|
|
||||||
|
export const setStore = records => {
|
||||||
|
RecordsStore.update(state => { state.records = records.allRecords });
|
||||||
|
RecordsStore.update(state => { state.center = records.center });
|
||||||
|
}
|
||||||
15
src/store/Selectors.js
Normal file
15
src/store/Selectors.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
|
const getState = state => state;
|
||||||
|
|
||||||
|
// General getters
|
||||||
|
export const fetchRecords = createSelector(getState, state => state.records);
|
||||||
|
|
||||||
|
// More specific getters
|
||||||
|
export const fetchRecord = recordId => createSelector(getState, state => {
|
||||||
|
|
||||||
|
return state.records.filter(record => record.id === recordId)[0];
|
||||||
|
});
|
||||||
|
// export const getPoll = pollId => createSelector(getState, state => state.polls.filter(poll => poll.id === parseInt(pollId))[0]);
|
||||||
|
// export const getChat = contactId => createSelector(getState, state => state.chats.filter(c => parseInt(c.contact_id) === parseInt(contactId))[0].chats);
|
||||||
|
// export const getContact = contactId => createSelector(getState, state => state.contacts.filter(c => parseInt(c.id) === parseInt(contactId))[0]);
|
||||||
Reference in New Issue
Block a user