Merge pull request #15 from Hzin/VAN-45-create-view-search-itinerary
Van 45 create view search itinerary
This commit is contained in:
11
craco.config.js
Normal file
11
craco.config.js
Normal file
@@ -0,0 +1,11 @@
|
||||
// craco.config.js
|
||||
module.exports = {
|
||||
style: {
|
||||
postcss: {
|
||||
plugins: [
|
||||
require('tailwindcss'),
|
||||
require('autoprefixer'),
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
28547
package-lock.json
generated
28547
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
21
package.json
21
package.json
@@ -8,6 +8,7 @@
|
||||
"@capacitor/haptics": "1.1.4",
|
||||
"@capacitor/keyboard": "1.2.2",
|
||||
"@capacitor/status-bar": "1.0.8",
|
||||
"@craco/craco": "^6.4.5",
|
||||
"@hookform/error-message": "^2.0.0",
|
||||
"@ionic-selectable/core": "^5.0.0-alpha.13",
|
||||
"@ionic/react": "^6.0.0",
|
||||
@@ -17,21 +18,23 @@
|
||||
"@testing-library/user-event": "^12.6.3",
|
||||
"@types/jest": "^26.0.20",
|
||||
"@types/node": "^12.19.15",
|
||||
"@types/react": "^16.14.3",
|
||||
"@types/react-dom": "^16.9.10",
|
||||
"@types/react": "^18.0.18",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"@types/react-router": "^5.1.11",
|
||||
"@types/react-router-dom": "^5.1.7",
|
||||
"axios": "^0.26.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"ionicons": "^5.4.0",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"pigeon-maps": "^0.21.0",
|
||||
"pullstate": "^1.24.0",
|
||||
"react": "^17.0.1",
|
||||
"react-dom": "^17.0.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-google-places-autocomplete": "^3.4.0",
|
||||
"react-hook-form": "^7.30.0",
|
||||
"react-router": "^5.2.0",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-scripts": "4.0.3",
|
||||
"reselect": "^4.1.5",
|
||||
"typescript": "^4.1.3",
|
||||
"web-vitals": "^0.2.4",
|
||||
@@ -50,8 +53,10 @@
|
||||
"yup": "^0.32.11"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"start": "cross-env TAILWIND_MODE=watch craco start",
|
||||
"build": "craco build",
|
||||
"ionic:serve": "cross-env TAILWIND_MODE=watch craco start",
|
||||
"ionic:build": "craco build",
|
||||
"test": "react-scripts test --transformIgnorePatterns 'node_modules/(?!(@ionic/react|@ionic/react-router|@ionic/core|@stencil/core|ionicons)/)'",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
@@ -77,7 +82,9 @@
|
||||
"@capacitor/cli": "3.4.3",
|
||||
"@types/axios": "^0.14.0",
|
||||
"@types/lodash.isequal": "^4.5.6",
|
||||
"react-scripts": "5.0.1"
|
||||
"autoprefixer": "^9.8.8",
|
||||
"postcss": "^7.0.39",
|
||||
"tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.2.17"
|
||||
},
|
||||
"description": "Projeto de conclusão de curso a fim de resolver a dificuldade de alunos universitários ao buscar vans para suas universidades"
|
||||
}
|
||||
|
||||
7
postcss.config.js
Normal file
7
postcss.config.js
Normal file
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
|
||||
154
src/App.tsx
154
src/App.tsx
@@ -1,4 +1,4 @@
|
||||
import { Redirect, Route } from 'react-router-dom';
|
||||
import React, { useContext, useState } from "react";
|
||||
import {
|
||||
IonApp,
|
||||
IonIcon,
|
||||
@@ -7,109 +7,125 @@ import {
|
||||
IonTabBar,
|
||||
IonTabButton,
|
||||
IonTabs,
|
||||
setupIonicReact
|
||||
} from '@ionic/react';
|
||||
import { IonReactRouter } from '@ionic/react-router';
|
||||
setupIonicReact,
|
||||
} from "@ionic/react";
|
||||
import { IonReactRouter } from "@ionic/react-router";
|
||||
import { Redirect, Route } from "react-router-dom";
|
||||
|
||||
import { home, person, search } from "ionicons/icons";
|
||||
|
||||
// importação das páginas
|
||||
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';
|
||||
import Debug from './pages/Debug';
|
||||
import Cadastro from "./pages/Cadastro/Cadastro";
|
||||
import CadastroCompletar from "./pages/CadastroCompletar/CadastroCompletar";
|
||||
import CompletarDocumento from "./pages/CadastroCompletar/CompletarDocumento";
|
||||
import CompletarTelefone from "./pages/CadastroCompletar/CompletarTelefone";
|
||||
|
||||
import Home from "./pages/Home";
|
||||
import Login from "./pages/Login";
|
||||
import Perfil from "./pages/Perfil";
|
||||
import PerfilEditar from "./pages/PerfilEditar";
|
||||
|
||||
import VeiculoCadastro from "./pages/VeiculoCadastro";
|
||||
import MeusVeiculos from "./pages/MeusVeiculos";
|
||||
import CadastrarItinerario from "./pages/CadastrarItinerario/CadastrarItinerario";
|
||||
import MeusItinerarios from "./pages/MeusItinerarios/MeusItinerarios";
|
||||
|
||||
import Buscas from "./pages/Buscas";
|
||||
import BuscarItinerario from "./pages/BuscarItinerario";
|
||||
import BuscarPassageiro from "./pages/BuscarPassageiro/BuscarPassageiro";
|
||||
import Transportes from "./pages/Transportes/Transportes";
|
||||
|
||||
/* Core CSS required for Ionic components to work properly */
|
||||
import '@ionic/react/css/core.css';
|
||||
import "@ionic/react/css/core.css";
|
||||
|
||||
/* Basic CSS for apps built with Ionic */
|
||||
import '@ionic/react/css/normalize.css';
|
||||
import '@ionic/react/css/structure.css';
|
||||
import '@ionic/react/css/typography.css';
|
||||
import "@ionic/react/css/normalize.css";
|
||||
import "@ionic/react/css/structure.css";
|
||||
import "@ionic/react/css/typography.css";
|
||||
|
||||
/* Optional CSS utils that can be commented out */
|
||||
import '@ionic/react/css/padding.css';
|
||||
import '@ionic/react/css/float-elements.css';
|
||||
import '@ionic/react/css/text-alignment.css';
|
||||
import '@ionic/react/css/text-transformation.css';
|
||||
import '@ionic/react/css/flex-utils.css';
|
||||
import '@ionic/react/css/display.css';
|
||||
import "@ionic/react/css/display.css";
|
||||
import "@ionic/react/css/flex-utils.css";
|
||||
import "@ionic/react/css/float-elements.css";
|
||||
import "@ionic/react/css/padding.css";
|
||||
import "@ionic/react/css/text-alignment.css";
|
||||
import "@ionic/react/css/text-transformation.css";
|
||||
|
||||
/* 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';
|
||||
import MeusItinerarios from './pages/MeusItinerarios/MeusItinerarios';
|
||||
import CadastrarItinerario from './pages/CadastrarItinerario/CadastrarItinerario';
|
||||
/* Tailwind styles */
|
||||
import "./theme/tailwind.css";
|
||||
|
||||
setupIonicReact();
|
||||
|
||||
const routes = (
|
||||
<>
|
||||
<Route exact path="/cadastro" component={Cadastro}></Route>
|
||||
<Route exact path="/login" component={Login}></Route>
|
||||
<>
|
||||
<Route exact path="/cadastro" component={Cadastro}></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="/home" component={Home}></Route>
|
||||
<Route exact path="/home" component={Home}></Route>
|
||||
<Route exact path="/login" component={Login}></Route>
|
||||
<Route exact path="/perfil" component={Perfil}></Route>
|
||||
<Route exact path="/usuario/:id" component={Perfil}></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="/perfil/editar" component={PerfilEditar}></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="/veiculos/cadastrar" component={VeiculoCadastro}></Route>
|
||||
<Route exact path="/veiculos/meus" component={MeusVeiculos}></Route>
|
||||
<Route
|
||||
exact
|
||||
path="/cadastrar-itinerario"
|
||||
component={CadastrarItinerario}
|
||||
></Route>
|
||||
<Route exact path="/meus-itinerarios" component={MeusItinerarios}></Route>
|
||||
|
||||
<Route exact path="/usuario/:id" component={Perfil}></Route>
|
||||
<Route exact path="/buscas" component={Buscas}></Route>
|
||||
<Route exact path="/buscar/itinerario" component={BuscarItinerario}></Route>
|
||||
<Route exact path="/buscar/passageiro" component={BuscarPassageiro}></Route>
|
||||
<Route exact path="/transportes" component={Transportes}></Route>
|
||||
|
||||
<Route exact path="/cadastro-van" component={CadastroVan}></Route>
|
||||
<Route exact path="/minhas-vans" component={MinhasVans}></Route>
|
||||
|
||||
<Route exact path="/cadastrar-itinerario" component={CadastrarItinerario}></Route>
|
||||
<Route exact path="/meus-itinerarios" component={MeusItinerarios}></Route>
|
||||
<Route exact path="/">
|
||||
<Redirect to="/login" />
|
||||
</Route>
|
||||
</>)
|
||||
<Route exact path="/">
|
||||
<Redirect to="/home" />
|
||||
</Route>
|
||||
</>
|
||||
);
|
||||
|
||||
interface IUserManager {
|
||||
setIsLoggedIn: Function;
|
||||
}
|
||||
|
||||
const user: IUserManager = {
|
||||
setIsLoggedIn: () => {}
|
||||
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>
|
||||
return (
|
||||
<IonApp>
|
||||
<IonReactRouter>
|
||||
{isLoggedIn ? (
|
||||
<IonTabs>
|
||||
<IonRouterOutlet>{routes}</IonRouterOutlet>
|
||||
|
||||
|
||||
<IonTabBar slot="bottom">
|
||||
<IonTabButton tab="buscar" href="/buscar-transporte">
|
||||
<IonTabButton tab="buscar" href="/buscas">
|
||||
<IonIcon icon={search} />
|
||||
<IonLabel>Buscar</IonLabel>
|
||||
</IonTabButton>
|
||||
@@ -123,11 +139,13 @@ const IonicApp: React.FC = () => {
|
||||
</IonTabButton>
|
||||
</IonTabBar>
|
||||
</IonTabs>
|
||||
) : (<IonRouterOutlet>{routes}</IonRouterOutlet>)}
|
||||
) : (
|
||||
<IonRouterOutlet>{routes}</IonRouterOutlet>
|
||||
)}
|
||||
</IonReactRouter>
|
||||
</IonApp>
|
||||
)
|
||||
}
|
||||
</IonApp>
|
||||
);
|
||||
};
|
||||
|
||||
const App: React.FC = () => {
|
||||
return (
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
import { IonCol, IonRouterLink, IonRow } from "@ionic/react";
|
||||
|
||||
interface ComponentProps {
|
||||
message: string,
|
||||
link: string,
|
||||
text: string
|
||||
message: string;
|
||||
link: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
export const Action = (props: ComponentProps) => (
|
||||
|
||||
<IonRow className="ion-text-center ion-justify-content-center">
|
||||
<IonCol size="12">
|
||||
{ props.message }
|
||||
<IonRouterLink className="custom-link" routerLink={ props.link }> { props.text } →</IonRouterLink>
|
||||
</IonCol>
|
||||
</IonRow>
|
||||
);
|
||||
<IonRow className="ion-text-center ion-justify-content-center">
|
||||
<IonCol size="12">
|
||||
{props.message}
|
||||
<IonRouterLink className="custom-link" routerLink={props.link}>
|
||||
{" "}
|
||||
{props.text} →
|
||||
</IonRouterLink>
|
||||
</IonCol>
|
||||
</IonRow>
|
||||
);
|
||||
|
||||
17
src/components/PageHeader.tsx
Normal file
17
src/components/PageHeader.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { IonBackButton, IonButtons, IonHeader, IonTitle, IonToolbar } from "@ionic/react";
|
||||
|
||||
interface ComponentProps {
|
||||
pageName: string;
|
||||
backButtonPageUrl?: string;
|
||||
}
|
||||
|
||||
export const PageHeader = (props: ComponentProps) => (
|
||||
<IonHeader translucent>
|
||||
<IonToolbar>
|
||||
<IonTitle>{props.pageName}</IonTitle>
|
||||
<IonButtons slot="start">
|
||||
<IonBackButton text="" defaultHref={ props.backButtonPageUrl ? props.backButtonPageUrl : undefined } />
|
||||
</IonButtons>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
);
|
||||
@@ -1,45 +0,0 @@
|
||||
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
|
||||
@@ -1,4 +1,4 @@
|
||||
const transportsRoutesDefault = '/transports';
|
||||
const transportsRoutesDefault = '/itineraries';
|
||||
const transportsRoutes = {
|
||||
create: {
|
||||
url: `${transportsRoutesDefault}`
|
||||
@@ -6,6 +6,9 @@ const transportsRoutes = {
|
||||
get: {
|
||||
url: `${transportsRoutesDefault}`
|
||||
},
|
||||
search: {
|
||||
url: `${transportsRoutesDefault}/search/inradius`
|
||||
},
|
||||
update: {
|
||||
url: `${transportsRoutesDefault}`
|
||||
},
|
||||
@@ -1,17 +1,17 @@
|
||||
const vansRoutesDefault = '/vans/locator';
|
||||
const vansRoutes = {
|
||||
const vehiclesRoutesDefault = '/vehicles/locator';
|
||||
const vehiclesRoutes = {
|
||||
list: {
|
||||
url: `${vansRoutesDefault}/list`
|
||||
url: `${vehiclesRoutesDefault}/list`
|
||||
},
|
||||
getById: {
|
||||
url: `${vansRoutesDefault}/`
|
||||
url: `${vehiclesRoutesDefault}/`
|
||||
},
|
||||
create: {
|
||||
url: `${vansRoutesDefault}/`
|
||||
url: `${vehiclesRoutesDefault}/`
|
||||
},
|
||||
update: {
|
||||
url: `${vansRoutesDefault}/edit`
|
||||
url: `${vehiclesRoutesDefault}/edit`
|
||||
}
|
||||
}
|
||||
|
||||
export default vansRoutes;
|
||||
export default vehiclesRoutes;
|
||||
@@ -1,20 +0,0 @@
|
||||
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;
|
||||
20
src/constants/routes/vehiclesRoutes.ts
Normal file
20
src/constants/routes/vehiclesRoutes.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
const vehiclesRoutesDefault = '/vehicles';
|
||||
const vehiclesRoutes = {
|
||||
list: {
|
||||
url: `${vehiclesRoutesDefault}/list`
|
||||
},
|
||||
getByPlate: {
|
||||
url: `${vehiclesRoutesDefault}/plate`
|
||||
},
|
||||
getByUserId: {
|
||||
url: `${vehiclesRoutesDefault}/user`
|
||||
},
|
||||
create: {
|
||||
url: `${vehiclesRoutesDefault}/`
|
||||
},
|
||||
update: {
|
||||
url: `${vehiclesRoutesDefault}/`
|
||||
}
|
||||
}
|
||||
|
||||
export default vehiclesRoutes;
|
||||
13
src/models/itinerary.model.ts
Normal file
13
src/models/itinerary.model.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export interface Itinerary {
|
||||
id_itinerary: number;
|
||||
vehicle_plate: string;
|
||||
price: number;
|
||||
days_of_week: number;
|
||||
specific_day: Date;
|
||||
estimated_departure_time: Date;
|
||||
estimated_arrival_time: Date;
|
||||
available_seats: number;
|
||||
itinerary_nickname: string;
|
||||
created_at: Date;
|
||||
updated_at: Date;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
export type Van = {
|
||||
export type Vehicle = {
|
||||
id: number;
|
||||
carPlate: string;
|
||||
carBrand: string;
|
||||
|
||||
@@ -1,20 +1,36 @@
|
||||
import {
|
||||
IonBackButton,
|
||||
IonButton,
|
||||
IonButtons,
|
||||
IonCard,
|
||||
IonCardContent,
|
||||
IonCardHeader,
|
||||
IonCardSubtitle,
|
||||
IonCardTitle,
|
||||
IonContent,
|
||||
IonHeader,
|
||||
IonIcon,
|
||||
IonItem,
|
||||
IonItemDivider,
|
||||
IonPage,
|
||||
IonRow,
|
||||
IonTitle,
|
||||
IonToast,
|
||||
IonToolbar,
|
||||
} from "@ionic/react";
|
||||
import {
|
||||
arrowForwardOutline,
|
||||
cashOutline,
|
||||
chevronForwardOutline,
|
||||
locateOutline,
|
||||
locationOutline,
|
||||
personOutline,
|
||||
starOutline,
|
||||
timeOutline,
|
||||
} from "ionicons/icons";
|
||||
import "./BuscarTransporte.css";
|
||||
import "./BuscarItinerario.css";
|
||||
|
||||
import itinerariesService from "../services/functions/itinerariesService";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { useHistory } from "react-router";
|
||||
@@ -23,17 +39,31 @@ import GooglePlacesAutocomplete, {
|
||||
geocodeByAddress,
|
||||
getLatLng,
|
||||
} from "react-google-places-autocomplete";
|
||||
import { Itinerary } from "../models/itinerary.model";
|
||||
import { PageHeader } from "../components/PageHeader";
|
||||
import { closeToast } from "../services/utils";
|
||||
|
||||
const BuscarTransporte: React.FC = () => {
|
||||
import { Color } from "@ionic/core";
|
||||
|
||||
const BuscarItinerario: React.FC = () => {
|
||||
const history = useHistory();
|
||||
|
||||
const [showToast, setShowToast] = useState(false);
|
||||
const [messageToast, setMessageToast] = useState("");
|
||||
const [toastColor, setToastColor] = useState<Color>("primary");
|
||||
|
||||
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 [addressResults, setAddressResults] = useState<any[]>([]);
|
||||
const [inputActive, setInputActive] = useState("");
|
||||
|
||||
const [recentSearches, setRecentSearches] = useState<any[]>([]);
|
||||
|
||||
const [itinerariesList, setItinerariesList] = useState<Itinerary[]>();
|
||||
|
||||
// const optionsAddress = async (inputValue: any) => {
|
||||
// let results = await autoCompleteAddress(inputValue)
|
||||
// .then((res) => {
|
||||
@@ -83,22 +113,61 @@ const BuscarTransporte: React.FC = () => {
|
||||
}
|
||||
}, [addressTo]);
|
||||
|
||||
function buscaTransporte() {
|
||||
if (coordinatesFrom && coordinatesTo && addressFrom && addressTo) {
|
||||
history.push({
|
||||
pathname: "/transportes",
|
||||
state: {
|
||||
coordinatesFrom,
|
||||
coordinatesTo,
|
||||
addressFrom,
|
||||
addressTo,
|
||||
},
|
||||
});
|
||||
async function buscarItinerarios() {
|
||||
if (!coordinatesFrom || !coordinatesTo || !addressFrom || !addressTo) {
|
||||
return;
|
||||
}
|
||||
|
||||
const maxRecentSearchesLength = 0
|
||||
|
||||
if (recentSearches.length >= maxRecentSearchesLength) {
|
||||
setRecentSearches(recentSearches.slice(recentSearches.length - maxRecentSearchesLength));
|
||||
}
|
||||
|
||||
setRecentSearches((arr) => [
|
||||
...arr,
|
||||
{
|
||||
addressFrom: addressFrom.label,
|
||||
addressTo: addressTo.label,
|
||||
time: Date.now(),
|
||||
},
|
||||
]);
|
||||
|
||||
await itinerariesService
|
||||
.searchItineraries({
|
||||
coordinatesFrom,
|
||||
coordinatesTo,
|
||||
})
|
||||
.then((response) => {
|
||||
// if (response.status === "error") {
|
||||
// setToastColor("danger");
|
||||
// setMessageToast(response.message);
|
||||
// setShowToast(true);
|
||||
|
||||
// return;
|
||||
// }
|
||||
|
||||
setItinerariesList(response);
|
||||
})
|
||||
.catch((err) => {
|
||||
setToastColor("danger");
|
||||
setMessageToast(err);
|
||||
setShowToast(true);
|
||||
});
|
||||
}
|
||||
|
||||
function fillSearchBars(addressFrom: string, addressTo: string) {
|
||||
// setAddressFrom(addressFrom);
|
||||
// setAddressTo(addressTo);
|
||||
}
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<PageHeader
|
||||
pageName="Buscar itinerários"
|
||||
backButtonPageUrl="/buscas"
|
||||
></PageHeader>
|
||||
|
||||
<IonContent fullscreen>
|
||||
<IonCard>
|
||||
<IonCardContent>
|
||||
@@ -141,32 +210,55 @@ const BuscarTransporte: React.FC = () => {
|
||||
/>
|
||||
</div>
|
||||
<div className="button-search">
|
||||
<IonButton color="primary" onClick={() => buscaTransporte()}>
|
||||
<IonButton color="primary" onClick={() => buscarItinerarios()}>
|
||||
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">
|
||||
|
||||
{recentSearches && recentSearches.length !== 0 ? (
|
||||
<>
|
||||
<IonItemDivider color="dark">Pesquisas recentes</IonItemDivider>
|
||||
<IonRow class="latest-searches">
|
||||
{recentSearches.map((search, index) => {
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<IonRow key={index}
|
||||
class="latest-searches"
|
||||
onClick={() => {
|
||||
fillSearchBars(search.addressFrom, search.addressTo);
|
||||
}}
|
||||
>
|
||||
<IonIcon
|
||||
className="icon-align-vcenter"
|
||||
size="large"
|
||||
icon={timeOutline}
|
||||
></IonIcon>
|
||||
<div className="div_from_to">
|
||||
<span>{search.addressFrom}</span>
|
||||
<IonIcon icon={arrowForwardOutline}></IonIcon>
|
||||
<span>{search.addressTo}</span>
|
||||
<br />
|
||||
<small>{search.time}</small>
|
||||
</div>
|
||||
<IonIcon
|
||||
className="icon-forward icon-align-vcenter"
|
||||
size="large"
|
||||
icon={chevronForwardOutline}
|
||||
></IonIcon>
|
||||
</IonRow>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
})}
|
||||
</IonRow>
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{/* <IonRow class="latest-searches">
|
||||
<IonIcon
|
||||
className="icon-align-vcenter"
|
||||
size="large"
|
||||
@@ -184,7 +276,7 @@ const BuscarTransporte: React.FC = () => {
|
||||
size="large"
|
||||
icon={chevronForwardOutline}
|
||||
/>
|
||||
</IonRow>
|
||||
</IonRow> */}
|
||||
{/* <IonModal isOpen={showModalEnd}>
|
||||
<IonContent>
|
||||
<div className="header-search-modal">
|
||||
@@ -225,9 +317,54 @@ const BuscarTransporte: React.FC = () => {
|
||||
)}
|
||||
</IonContent>
|
||||
</IonModal> */}
|
||||
|
||||
{itinerariesList && itinerariesList.length !== 0 ? (
|
||||
<>
|
||||
<IonItemDivider color="secondary">Resultados</IonItemDivider>
|
||||
{itinerariesList.map((itinerary, index) => {
|
||||
return (
|
||||
<IonCard
|
||||
button
|
||||
key={index}
|
||||
onClick={() => {
|
||||
history.push(`/itinerary/${itinerary.id_itinerary}`);
|
||||
}}
|
||||
>
|
||||
<IonCardHeader>
|
||||
<IonCardTitle>{itinerary.itinerary_nickname}</IonCardTitle>
|
||||
<IonCardSubtitle>
|
||||
<p>
|
||||
<IonIcon icon={personOutline} /> Vagas disponíveis:{" "}
|
||||
{itinerary.available_seats}
|
||||
</p>
|
||||
<p>
|
||||
<IonIcon icon={starOutline} /> Motorista:{" "}
|
||||
{itinerary.price}
|
||||
</p>
|
||||
<p>
|
||||
<IonIcon icon={cashOutline} /> Valor:{" "}
|
||||
{itinerary.vehicle_plate}
|
||||
</p>
|
||||
</IonCardSubtitle>
|
||||
</IonCardHeader>
|
||||
</IonCard>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
<IonToast
|
||||
color={toastColor}
|
||||
isOpen={showToast}
|
||||
onDidDismiss={() => closeToast(setShowToast)}
|
||||
message={messageToast}
|
||||
duration={2500}
|
||||
/>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default BuscarTransporte;
|
||||
export default BuscarItinerario;
|
||||
@@ -1,127 +1,163 @@
|
||||
import { IonContent, IonPage, IonFab, IonFabButton, IonIcon } from '@ionic/react';
|
||||
import { search } from 'ionicons/icons';
|
||||
import './BuscarPassageiro.css';
|
||||
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 { 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';
|
||||
import RecordsStore from "../../store/RecordsStore";
|
||||
import { fetchRecords } from "../../store/Selectors";
|
||||
import { getUsersSearching } from "../../services/api/users";
|
||||
import { UserSearchInfos } from "../../components/UserSearchInfos/UserSearchInfos";
|
||||
import { PageHeader } from "../../components/PageHeader";
|
||||
|
||||
const maptilerProvider = maptiler('d5JQJPLLuap8TkJJlTdJ', 'streets');
|
||||
const maptilerProvider = maptiler("d5JQJPLLuap8TkJJlTdJ", "streets");
|
||||
|
||||
const BuscarPassageiro: React.FC = () => {
|
||||
// UNCOMMENT THESE TO USE CURRENT LOCATION.
|
||||
|
||||
// UNCOMMENT THESE TO USE CURRENT LOCATION.
|
||||
// const [ currentPoint, setCurrentPoint ] = useState(false);
|
||||
|
||||
// const [ currentPoint, setCurrentPoint ] = useState(false);
|
||||
// useEffect(() => {
|
||||
|
||||
// useEffect(() => {
|
||||
// const getCurrentLocation = async () => {
|
||||
|
||||
// const getCurrentLocation = async () => {
|
||||
// const fetchedLocation = await getLocation();
|
||||
// setCurrentPoint(fetchedLocation.currentLocation);
|
||||
// }
|
||||
|
||||
// const fetchedLocation = await getLocation();
|
||||
// setCurrentPoint(fetchedLocation.currentLocation);
|
||||
// }
|
||||
// getCurrentLocation();
|
||||
// }, []);
|
||||
|
||||
// getCurrentLocation();
|
||||
// }, []);
|
||||
// useIonViewWillEnter(() => {
|
||||
|
||||
// useIonViewWillEnter(() => {
|
||||
// getUsersSearching(currentPoint);
|
||||
// });
|
||||
|
||||
// getUsersSearching(currentPoint);
|
||||
// });
|
||||
const [currentPoint, setCurrentPoint] = useState({
|
||||
latitude: -22.907829,
|
||||
longitude: -47.062943,
|
||||
});
|
||||
|
||||
const [ currentPoint, setCurrentPoint ] = useState({ latitude: -22.907829, longitude: -47.062943 });
|
||||
const records = RecordsStore.useState(fetchRecords);
|
||||
const center = { 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 [ results, setResults ] = useState([]);
|
||||
const [ zoom, setZoom ] = useState(14);
|
||||
const [moveMode, setMoveMode] = useState(false);
|
||||
|
||||
const [ moveMode, setMoveMode ] = useState(false);
|
||||
// useEffect(() => {
|
||||
|
||||
// useEffect(() => {
|
||||
// const getData = async () => {
|
||||
|
||||
// const getData = async () => {
|
||||
// await getUsersSearching(currentPoint);
|
||||
// }
|
||||
|
||||
// await getUsersSearching(currentPoint);
|
||||
// }
|
||||
// getData();
|
||||
// }, [ currentPoint ]);
|
||||
|
||||
// getData();
|
||||
// }, [ currentPoint ]);
|
||||
useEffect(() => {
|
||||
setResults(records);
|
||||
}, [records]);
|
||||
|
||||
useEffect(() => {
|
||||
const hideMarkers = () => {
|
||||
console.log("entrou");
|
||||
const tempRecords = JSON.parse(JSON.stringify(results));
|
||||
tempRecords.forEach((tempRecord: any) => (tempRecord.showInfo = false));
|
||||
console.log(tempRecords);
|
||||
setResults(tempRecords);
|
||||
};
|
||||
|
||||
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) => {
|
||||
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 showMarkerInfo = (e: any, index: any) => {
|
||||
const tempRecords = JSON.parse(JSON.stringify(results));
|
||||
|
||||
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;
|
||||
|
||||
// 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);
|
||||
}
|
||||
console.log(tempRecords);
|
||||
setResults(tempRecords);
|
||||
};
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonContent fullscreen>
|
||||
{/* { results &&
|
||||
<PageHeader
|
||||
pageName="Buscar passageiros"
|
||||
backButtonPageUrl="/buscas"
|
||||
></PageHeader>
|
||||
<IonContent fullscreen>
|
||||
{/* { results &&
|
||||
<> */}
|
||||
<Map onBoundsChanged={e => handleMap(e)} defaultCenter={ [center.latitude, center.longitude] } defaultZoom={ zoom } provider={ maptilerProvider } touchEvents={ true }>
|
||||
<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 && 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>
|
||||
|
||||
{ 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>
|
||||
<IonFab vertical="bottom" horizontal="end" slot="fixed">
|
||||
<IonFabButton>
|
||||
<IonIcon onClick={searchResults} icon={search} />
|
||||
</IonFabButton>
|
||||
</IonFab>
|
||||
{/* </> */}
|
||||
{/* } */}
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default BuscarPassageiro;
|
||||
export default BuscarPassageiro;
|
||||
|
||||
64
src/pages/Buscas.tsx
Normal file
64
src/pages/Buscas.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import { IonCard, IonCardContent, IonCardHeader, IonCardSubtitle, IonCardTitle, IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from "@ionic/react";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { useHistory } from "react-router";
|
||||
import { PageHeader } from "../components/PageHeader";
|
||||
|
||||
const Buscas: React.FC = () => {
|
||||
useEffect(() => {}, []);
|
||||
const history = useHistory();
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<PageHeader
|
||||
pageName="Buscas"
|
||||
></PageHeader>
|
||||
|
||||
<IonContent fullscreen>
|
||||
<IonCard button class="cardItem" onClick={ () => history.push({ pathname: "/buscar/itinerario"}) }>
|
||||
<img src="https://images.unsplash.com/photo-1561361513-2d000a50f0dc?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8MXx8dmFufGVufDB8fDB8fA%3D%3D&w=1000&q=80" />
|
||||
<IonCardHeader>
|
||||
<IonCardTitle>Buscar itinerários</IonCardTitle>
|
||||
</IonCardHeader>
|
||||
|
||||
<IonCardContent>
|
||||
Clique aqui para buscar por itinerários
|
||||
</IonCardContent>
|
||||
</IonCard>
|
||||
|
||||
<IonCard button class="cardItem" onClick={ () => history.push({ pathname: "/buscar/passageiro"}) }>
|
||||
<img src="https://media.istockphoto.com/photos/male-passenger-using-laptop-during-flight-picture-id926203958?k=20&m=926203958&s=612x612&w=0&h=o52_eychVRRum6U5Q8C3bVxpnyXzcueqo1I52bhI-KA=" />
|
||||
<IonCardHeader>
|
||||
<IonCardTitle>Buscar passageiros</IonCardTitle>
|
||||
</IonCardHeader>
|
||||
|
||||
<IonCardContent>
|
||||
Clique aqui para buscar por passageiros
|
||||
</IonCardContent>
|
||||
</IonCard>
|
||||
|
||||
{/* <IonCard button onClick={ () => history.push({ pathname: "/buscar-transporte"}) }>
|
||||
<IonCardHeader>
|
||||
<IonCardTitle>/buscar-transporte</IonCardTitle>
|
||||
</IonCardHeader>
|
||||
|
||||
<IonCardContent>
|
||||
Clique aqui para buscar por transportes
|
||||
</IonCardContent>
|
||||
</IonCard>
|
||||
|
||||
<IonCard button onClick={ () => history.push({ pathname: "/buscar-passageiro"}) }>
|
||||
<IonCardHeader>
|
||||
<IonCardTitle>/buscar-passageiro</IonCardTitle>
|
||||
</IonCardHeader>
|
||||
|
||||
<IonCardContent>
|
||||
Clique aqui para buscar por passageiros
|
||||
</IonCardContent>
|
||||
</IonCard> */}
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default Buscas;
|
||||
@@ -2,97 +2,54 @@ import {
|
||||
IonBackButton,
|
||||
IonButton,
|
||||
IonButtons,
|
||||
IonCard,
|
||||
IonCardContent,
|
||||
IonContent,
|
||||
IonHeader,
|
||||
IonIcon,
|
||||
IonItem,
|
||||
IonLabel,
|
||||
IonList,
|
||||
IonListHeader,
|
||||
IonPage,
|
||||
IonRadio,
|
||||
IonRadioGroup,
|
||||
IonTitle,
|
||||
IonToolbar,
|
||||
} from "@ionic/react";
|
||||
import { close, locateOutline, locationOutline } from "ionicons/icons";
|
||||
import { useState } from "react";
|
||||
import GooglePlacesAutocomplete from "react-google-places-autocomplete";
|
||||
import { PageHeader } from "../../components/PageHeader";
|
||||
|
||||
export default function CadastrarItinerario() {
|
||||
const [selected, setSelected] = useState<any>("");
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonHeader>
|
||||
<IonToolbar>
|
||||
<IonButtons slot="start">
|
||||
<IonBackButton icon={close} text="" defaultHref="/perfil" />
|
||||
</IonButtons>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
<PageHeader
|
||||
pageName="Cadastrar itinerário"
|
||||
backButtonPageUrl="/perfil"
|
||||
></PageHeader>
|
||||
|
||||
<IonContent fullscreen>
|
||||
<IonHeader collapse="condense">
|
||||
<IonToolbar>
|
||||
<IonTitle size="large">Cadastrar Itinerário</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
<IonCard>
|
||||
<IonCardContent>
|
||||
<div className="inputs-from-to">
|
||||
<IonIcon icon={locateOutline}></IonIcon>
|
||||
<GooglePlacesAutocomplete
|
||||
apiKey={process.env.REACT_APP_KEY_API}
|
||||
apiOptions={{ language: "pt-br", region: "br" }}
|
||||
selectProps={{
|
||||
className: "input-autocomplete",
|
||||
placeholder: "R. José Paulino, 1234",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="inputs-from-to">
|
||||
<IonIcon icon={locationOutline}></IonIcon>
|
||||
<GooglePlacesAutocomplete
|
||||
apiKey={process.env.REACT_APP_KEY_API}
|
||||
apiOptions={{ language: "pt-br", region: "br" }}
|
||||
selectProps={{
|
||||
className: "input-autocomplete",
|
||||
placeholder: "PUC Campinas",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<IonList>
|
||||
<IonRadioGroup
|
||||
value={selected}
|
||||
onIonChange={(e) => setSelected(e.detail.value)}
|
||||
>
|
||||
<IonListHeader>
|
||||
<IonLabel>Name</IonLabel>
|
||||
</IonListHeader>
|
||||
|
||||
<IonItem>
|
||||
<IonLabel>Biff</IonLabel>
|
||||
<IonRadio slot="start" value="biff" />
|
||||
</IonItem>
|
||||
|
||||
<IonItem>
|
||||
<IonLabel>Griff</IonLabel>
|
||||
<IonRadio slot="start" value="griff" />
|
||||
</IonItem>
|
||||
|
||||
<IonItem>
|
||||
<IonLabel>Buford</IonLabel>
|
||||
<IonRadio slot="start" value="buford" />
|
||||
</IonItem>
|
||||
</IonRadioGroup>
|
||||
</IonList>
|
||||
<div className="button-search">
|
||||
<IonButton color="primary">Cadastrar</IonButton>
|
||||
</div>
|
||||
</IonCardContent>
|
||||
</IonCard>
|
||||
<div className="m-3">
|
||||
<h1>Digite o endereço de onde você iniciará a rota do itinerário</h1>
|
||||
<div className="inputs-from-to">
|
||||
<IonIcon icon={locateOutline}></IonIcon>
|
||||
<GooglePlacesAutocomplete
|
||||
apiKey={process.env.REACT_APP_KEY_API}
|
||||
apiOptions={{ language: "pt-br", region: "br" }}
|
||||
selectProps={{
|
||||
className: "input-autocomplete",
|
||||
placeholder: "R. José Paulino, 1234",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="inputs-from-to">
|
||||
<IonIcon icon={locationOutline}></IonIcon>
|
||||
<GooglePlacesAutocomplete
|
||||
apiKey={process.env.REACT_APP_KEY_API}
|
||||
apiOptions={{ language: "pt-br", region: "br" }}
|
||||
selectProps={{
|
||||
className: "input-autocomplete",
|
||||
placeholder: "PUC Campinas",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="button-search">
|
||||
<IonButton color="primary">Cadastrar</IonButton>
|
||||
</div>
|
||||
</div>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
|
||||
@@ -9,6 +9,8 @@ import * as UsersService from '../../services/api/users'
|
||||
import LocalStorage from '../../LocalStorage';
|
||||
import { UserContext } from '../../App';
|
||||
import { Color } from '@ionic/core';
|
||||
import { closeToast } from '../../services/utils';
|
||||
import { PageHeader } from '../../components/PageHeader';
|
||||
|
||||
const Cadastro: React.FC = () => {
|
||||
const history = useHistory();
|
||||
@@ -128,13 +130,11 @@ const Cadastro: React.FC = () => {
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonHeader>
|
||||
<IonToolbar>
|
||||
<IonButtons slot="start">
|
||||
<IonBackButton text={''} icon={arrowBack} defaultHref='login' />
|
||||
</IonButtons>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
<PageHeader
|
||||
pageName="Cadastro"
|
||||
backButtonPageUrl="/login"
|
||||
></PageHeader>
|
||||
|
||||
<IonContent fullscreen>
|
||||
<IonGrid className="ion-padding">
|
||||
<IonRow>
|
||||
@@ -216,7 +216,7 @@ const Cadastro: React.FC = () => {
|
||||
<IonToast
|
||||
color={toastColor}
|
||||
isOpen={showToast}
|
||||
onDidDismiss={() => setShowToast(false)}
|
||||
onDidDismiss={() => closeToast(setShowToast)}
|
||||
message={messageToast}
|
||||
duration={2500}
|
||||
/>
|
||||
|
||||
@@ -21,6 +21,8 @@ import { callOutline, documentTextOutline } from "ionicons/icons";
|
||||
|
||||
import '../Cadastro/Cadastro.css'
|
||||
import { Color } from "@ionic/core";
|
||||
import { closeToast } from "../../services/utils";
|
||||
import { PageHeader } from "../../components/PageHeader";
|
||||
|
||||
interface cardItem {
|
||||
icon: string;
|
||||
@@ -103,14 +105,10 @@ const CadastroCompletar: React.FC = () => {
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonHeader>
|
||||
<IonToolbar>
|
||||
<IonTitle>Completar cadastro</IonTitle>
|
||||
<IonButtons slot="start">
|
||||
<IonBackButton defaultHref="/perfil" />
|
||||
</IonButtons>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
<PageHeader
|
||||
pageName="Completar cadastro"
|
||||
backButtonPageUrl="/perfil"
|
||||
></PageHeader>
|
||||
|
||||
<IonContent>
|
||||
{ items.map((item, index) => {
|
||||
@@ -130,7 +128,7 @@ const CadastroCompletar: React.FC = () => {
|
||||
position="top"
|
||||
color={toastColor}
|
||||
isOpen={showToast}
|
||||
onDidDismiss={() => setShowToast(false)}
|
||||
onDidDismiss={() => closeToast(setShowToast)}
|
||||
message={toastMessage}
|
||||
duration={2500}
|
||||
/>
|
||||
|
||||
@@ -28,6 +28,8 @@ import { saveOutline } from "ionicons/icons";
|
||||
import * as usersRoutes from '../../services/api/users';
|
||||
|
||||
import validateCpf from '../../services/validateCpf'
|
||||
import { closeToast } from "../../services/utils";
|
||||
import { PageHeader } from "../../components/PageHeader";
|
||||
|
||||
interface documentTypesInterface {
|
||||
label: string;
|
||||
@@ -154,22 +156,12 @@ const CompletarDocumento: React.FC = () => {
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonHeader>
|
||||
<IonToolbar>
|
||||
<IonTitle>Completar cadastro</IonTitle>
|
||||
<IonButtons slot="start">
|
||||
<IonBackButton defaultHref="/perfil/completar" />
|
||||
</IonButtons>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
<PageHeader
|
||||
pageName="Completar cadastro"
|
||||
backButtonPageUrl="/perfil/completar"
|
||||
></PageHeader>
|
||||
|
||||
<IonContent fullscreen>
|
||||
<IonHeader collapse="condense">
|
||||
<IonToolbar>
|
||||
<IonTitle size="large">Completar cadastro</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonGrid>
|
||||
<IonRow>
|
||||
<IonCol>
|
||||
@@ -206,7 +198,7 @@ const CompletarDocumento: React.FC = () => {
|
||||
position="top"
|
||||
color='danger'
|
||||
isOpen={showToast}
|
||||
onDidDismiss={() => setShowToast(false)}
|
||||
onDidDismiss={() => closeToast(setShowToast)}
|
||||
message={messageToast}
|
||||
duration={2500}
|
||||
/>
|
||||
|
||||
@@ -25,6 +25,8 @@ import { saveOutline } from "ionicons/icons";
|
||||
|
||||
import * as usersRoutes from '../../services/api/users';
|
||||
import { Color } from "@ionic/core";
|
||||
import { closeToast } from "../../services/utils";
|
||||
import { PageHeader } from "../../components/PageHeader";
|
||||
|
||||
interface documentTypesInterface {
|
||||
label: string;
|
||||
@@ -112,22 +114,12 @@ const CompletarTelefone: React.FC = () => {
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonHeader>
|
||||
<IonToolbar>
|
||||
<IonTitle>Completar cadastro</IonTitle>
|
||||
<IonButtons slot="start">
|
||||
<IonBackButton defaultHref="/perfil/completar" />
|
||||
</IonButtons>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
<PageHeader
|
||||
pageName="Completar cadastro"
|
||||
backButtonPageUrl="/perfil/completar"
|
||||
></PageHeader>
|
||||
|
||||
<IonContent fullscreen>
|
||||
<IonHeader collapse="condense">
|
||||
<IonToolbar>
|
||||
<IonTitle size="large">Completar cadastro</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonGrid>
|
||||
<IonRow>
|
||||
<IonCol>
|
||||
@@ -156,7 +148,7 @@ const CompletarTelefone: React.FC = () => {
|
||||
position="top"
|
||||
color={toastColor}
|
||||
isOpen={showToast}
|
||||
onDidDismiss={() => setShowToast(false)}
|
||||
onDidDismiss={() => closeToast(setShowToast)}
|
||||
message={messageToast}
|
||||
duration={2500}
|
||||
/>
|
||||
|
||||
@@ -1,376 +0,0 @@
|
||||
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/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 [carBrands, setCarBrands] = useState([{
|
||||
codigo: '',
|
||||
nome: ''
|
||||
}]);
|
||||
|
||||
const [carModels, setCarModels] = useState([{
|
||||
codigo: '',
|
||||
nome: ''
|
||||
}]);
|
||||
|
||||
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 loadCarModels = async (carBrandId: string) => {
|
||||
const carModelsRes = await carsService.getCarModels(carBrandId)
|
||||
|
||||
if (carModelsRes.error) {
|
||||
setToastColor("danger")
|
||||
setToastMessage(carModelsRes.error.errorMessage);
|
||||
setInputValues({ carBrand: '' })
|
||||
return
|
||||
}
|
||||
|
||||
if (carModelsRes.data) {
|
||||
setCarModels(carModelsRes.data)
|
||||
}
|
||||
}
|
||||
|
||||
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: '/minhas-vans', state: {
|
||||
redirectData: {
|
||||
showToastMessage: true,
|
||||
toastColor: "success",
|
||||
toastMessage: response.message,
|
||||
},
|
||||
}})
|
||||
}).catch((err) => {
|
||||
setToastColor("danger")
|
||||
setToastMessage(err);
|
||||
setShowToast(true);
|
||||
})
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
let isMounted = true
|
||||
|
||||
const getCarsBrands = async () => {
|
||||
const carBrandsRes = await carsService.getAllCarBrands()
|
||||
|
||||
if (carBrandsRes.error) {
|
||||
setToastColor("danger")
|
||||
setToastMessage(carBrandsRes.error.errorMessage);
|
||||
setShowToast(true);
|
||||
return
|
||||
}
|
||||
|
||||
if (carBrandsRes.data) {
|
||||
if (isMounted) {
|
||||
setCarBrands(carBrandsRes.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getCarsBrands()
|
||||
|
||||
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 }); loadCarModels(e.detail.value) }}>
|
||||
{ carBrands ? carBrands.map((carBrand, index) => {
|
||||
return (<IonSelectOption key={index} value={index}>{carBrand.nome}</IonSelectOption>)
|
||||
}) : <></> }
|
||||
</IonSelect>
|
||||
</IonItem>
|
||||
|
||||
{ inputValues.carBrand ?
|
||||
<IonItem>
|
||||
<IonLabel>Modelo</IonLabel>
|
||||
<IonSelect onIonChange={(e: any) => { setInputValues({ carModel: e.detail.value }) }}>
|
||||
{ carModels ? carModels.map((carModel, index) => {
|
||||
return (<IonSelectOption key={index} value={carModel.nome}>{carModel.nome}</IonSelectOption>)
|
||||
}) : <></> }
|
||||
</IonSelect>
|
||||
</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,60 +1,58 @@
|
||||
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";
|
||||
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 { UserContext } from "../App";
|
||||
|
||||
const Debug: React.FC = () => {
|
||||
const [input, setInput] = useState('');
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonHeader>
|
||||
import { UserContext } from "../App";
|
||||
import { PageHeader } from "../components/PageHeader";
|
||||
|
||||
const Debug: React.FC = () => {
|
||||
const [input, setInput] = useState("");
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<PageHeader pageName="Debug" backButtonPageUrl="/home"></PageHeader>
|
||||
|
||||
<IonContent fullscreen>
|
||||
<IonHeader collapse="condense">
|
||||
<IonToolbar>
|
||||
<IonTitle>Debug</IonTitle>
|
||||
<IonTitle size="large">Debug</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonContent fullscreen>
|
||||
<IonHeader collapse="condense">
|
||||
<IonToolbar>
|
||||
<IonTitle size="large">Debug</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonGrid>
|
||||
<IonRow>
|
||||
<IonCol>
|
||||
<IonItem>
|
||||
<IonLabel position="floating"> Input</IonLabel>
|
||||
<IonInput
|
||||
type="date"
|
||||
value={input}
|
||||
// onIonChange={(e) => { setInput(e.detail.value!) }}
|
||||
></IonInput>
|
||||
</IonItem>
|
||||
|
||||
<IonButton onClick={(e) => { setInput('1994-12-15'); console.log(input) }}>Enviar</IonButton>
|
||||
</IonCol>
|
||||
</IonRow>
|
||||
</IonGrid>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default Debug;
|
||||
|
||||
<IonGrid>
|
||||
<IonRow>
|
||||
<IonCol>
|
||||
<IonItem>
|
||||
<IonLabel position="floating"> Input</IonLabel>
|
||||
<IonInput
|
||||
type="date"
|
||||
value={input}
|
||||
// onIonChange={(e) => { setInput(e.detail.value!) }}
|
||||
></IonInput>
|
||||
</IonItem>
|
||||
|
||||
<IonButton
|
||||
onClick={(e) => {
|
||||
setInput("1994-12-15");
|
||||
console.log(input);
|
||||
}}
|
||||
>
|
||||
Enviar
|
||||
</IonButton>
|
||||
</IonCol>
|
||||
</IonRow>
|
||||
</IonGrid>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default Debug;
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { IonContent, IonPage, IonToast } from '@ionic/react';
|
||||
import { Color } from '@ionic/core';
|
||||
import { useContext, useEffect, useState } from 'react';
|
||||
import { useLocation } from 'react-router';
|
||||
import { useHistory, useLocation } from 'react-router';
|
||||
|
||||
import { UserContext } from '../App';
|
||||
|
||||
import * as sessionRoutes from '../services/api/session';
|
||||
import { closeToast } from '../services/utils';
|
||||
|
||||
interface LocationState {
|
||||
redirectData?: {
|
||||
@@ -17,6 +18,7 @@ interface LocationState {
|
||||
|
||||
const Home: React.FC = () => {
|
||||
const location = useLocation<LocationState>();
|
||||
const history = useHistory();
|
||||
|
||||
const user = useContext(UserContext);
|
||||
|
||||
@@ -41,6 +43,7 @@ const Home: React.FC = () => {
|
||||
// setMessageToast(response.message);
|
||||
// setShowToast(true);
|
||||
|
||||
history.push(`/login`)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -52,11 +55,12 @@ const Home: React.FC = () => {
|
||||
// if (error.response.data.message) {
|
||||
console.dir('Houve um erro: ', { error })
|
||||
alert('Houve um erro')
|
||||
history.push(`/login`)
|
||||
})
|
||||
}
|
||||
|
||||
refreshUserToken()
|
||||
}, [])
|
||||
}, [location.state, user, history])
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
@@ -65,7 +69,7 @@ const Home: React.FC = () => {
|
||||
position="top"
|
||||
color={toastColor}
|
||||
isOpen={showToast}
|
||||
onDidDismiss={() => setShowToast(false)}
|
||||
onDidDismiss={() => closeToast(setShowToast)}
|
||||
message={toastMessage}
|
||||
duration={2500}
|
||||
/>
|
||||
|
||||
147
src/pages/Itinerario.tsx
Normal file
147
src/pages/Itinerario.tsx
Normal file
@@ -0,0 +1,147 @@
|
||||
import {
|
||||
IonBackButton,
|
||||
IonButtons,
|
||||
IonCard,
|
||||
IonCardContent,
|
||||
IonCardHeader,
|
||||
IonCardSubtitle,
|
||||
IonCardTitle,
|
||||
IonContent,
|
||||
IonHeader,
|
||||
IonIcon,
|
||||
IonItem,
|
||||
IonLabel,
|
||||
IonPage,
|
||||
IonTitle,
|
||||
IonToast,
|
||||
IonToolbar,
|
||||
} from "@ionic/react";
|
||||
import { Color } from "@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 vehiclesRoutes from "../services/api/vehicles";
|
||||
|
||||
import sessionsService from "../services/functions/sessionsService";
|
||||
import { closeToast } from "../services/utils";
|
||||
import { PageHeader } from "../components/PageHeader";
|
||||
|
||||
interface VehicleInfo {
|
||||
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 Itinerario: React.FC = () => {
|
||||
const history = useHistory();
|
||||
|
||||
const [showToast, setShowToast] = useState(false);
|
||||
const [toastMessage, setToastMessage] = useState("");
|
||||
const [toastColor, setToastColor] = useState<Color>("primary");
|
||||
|
||||
const [userVehicles, setUserVehicles] = useState<VehicleInfo[]>();
|
||||
|
||||
const redirectUserToLogin = () => {
|
||||
history.push({ pathname: "/login" });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const getUserVehicles = async () => {
|
||||
let userId = "";
|
||||
|
||||
const refreshSessionRes = await sessionsService.refreshSession();
|
||||
|
||||
if (refreshSessionRes.error) {
|
||||
redirectUserToLogin();
|
||||
return;
|
||||
}
|
||||
|
||||
if (refreshSessionRes.userId) {
|
||||
userId = refreshSessionRes.userId;
|
||||
}
|
||||
|
||||
vehiclesRoutes
|
||||
.getByUserId(userId)
|
||||
.then((response) => {
|
||||
if (response.status === "error") {
|
||||
setToastColor("danger");
|
||||
setToastMessage(response.message);
|
||||
setShowToast(true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setUserVehicles(response.data);
|
||||
})
|
||||
.catch((err) => {
|
||||
setToastColor("danger");
|
||||
setToastMessage(err);
|
||||
setShowToast(true);
|
||||
});
|
||||
};
|
||||
|
||||
getUserVehicles();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<PageHeader
|
||||
pageName="Minhas vehicles"
|
||||
backButtonPageUrl="/perfil"
|
||||
></PageHeader>
|
||||
|
||||
<IonContent>
|
||||
{userVehicles ? (
|
||||
userVehicles.map((vehicle, index) => {
|
||||
return (
|
||||
<IonCard key={index}>
|
||||
<IonCardHeader>
|
||||
<IonCardTitle>{vehicle.plate}</IonCardTitle>
|
||||
<IonCardSubtitle>
|
||||
{vehicle.brand} - {vehicle.model}
|
||||
</IonCardSubtitle>
|
||||
</IonCardHeader>
|
||||
{vehicle.locator_name ? (
|
||||
<>
|
||||
<IonCardContent>
|
||||
{vehicle.seats_number} assentos - Locador: {vehicle.locator_name}
|
||||
</IonCardContent>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<IonCardContent>
|
||||
{vehicle.seats_number} assentos - Não é alugado
|
||||
</IonCardContent>
|
||||
</>
|
||||
)}
|
||||
</IonCard>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
<IonToast
|
||||
position="top"
|
||||
color={toastColor}
|
||||
isOpen={showToast}
|
||||
onDidDismiss={() => closeToast(setShowToast)}
|
||||
message={toastMessage}
|
||||
duration={2500}
|
||||
/>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default Itinerario;
|
||||
@@ -3,30 +3,27 @@ import {
|
||||
IonHeader,
|
||||
IonPage,
|
||||
IonTitle,
|
||||
IonToolbar
|
||||
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 { IonItem, IonLabel, IonInput, IonButton } from "@ionic/react";
|
||||
|
||||
import * as sessionRoutes from '../services/api/session';
|
||||
import LocalStorage from '../LocalStorage';
|
||||
import * as sessionRoutes from "../services/api/session";
|
||||
import LocalStorage from "../LocalStorage";
|
||||
import { Action } from "../components/Action";
|
||||
import { UserContext } from "../App";
|
||||
import { closeToast } from "../services/utils";
|
||||
import { PageHeader } from "../components/PageHeader";
|
||||
|
||||
const Page: React.FC = () => {
|
||||
const [showToast, setShowToast] = useState(false);
|
||||
const [messageToast, setMessageToast ] = useState('');
|
||||
|
||||
const [messageToast, setMessageToast] = useState("");
|
||||
|
||||
const history = useHistory();
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
|
||||
const user = useContext(UserContext);
|
||||
|
||||
@@ -56,18 +53,18 @@ const Page: React.FC = () => {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(password.length < 7 || password.length > 12) {
|
||||
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
|
||||
return;
|
||||
}
|
||||
|
||||
const singinForm = {
|
||||
@@ -75,50 +72,48 @@ const Page: React.FC = () => {
|
||||
password: password,
|
||||
};
|
||||
|
||||
await sessionRoutes.create(singinForm).then(response => {
|
||||
if (response.status === 'error') {
|
||||
setMessageToast(response.message);
|
||||
setShowToast(true);
|
||||
await sessionRoutes
|
||||
.create(singinForm)
|
||||
.then((response) => {
|
||||
if (response.status === "error") {
|
||||
setMessageToast(response.message);
|
||||
setShowToast(true);
|
||||
|
||||
return
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const { token } = response.token
|
||||
const { token } = response.token;
|
||||
|
||||
LocalStorage.setToken(token);
|
||||
LocalStorage.setToken(token);
|
||||
|
||||
user.setIsLoggedIn(true);
|
||||
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
|
||||
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')
|
||||
})
|
||||
// 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>
|
||||
<PageHeader pageName="Login"></PageHeader>
|
||||
|
||||
<IonContent fullscreen>
|
||||
<IonHeader collapse="condense">
|
||||
<IonToolbar>
|
||||
<IonTitle size="large">Login</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonGrid>
|
||||
<IonRow>
|
||||
<IonCol>
|
||||
@@ -152,7 +147,11 @@ const Page: React.FC = () => {
|
||||
Login
|
||||
</IonButton>
|
||||
<p style={{ fontSize: "medium" }}>
|
||||
<Action message="Ainda não possui uma conta?" text="Cadastre-se aqui!" link="/cadastro" />
|
||||
<Action
|
||||
message="Ainda não possui uma conta?"
|
||||
text="Cadastre-se aqui!"
|
||||
link="/cadastro"
|
||||
/>
|
||||
</p>
|
||||
</IonCol>
|
||||
</IonRow>
|
||||
@@ -160,9 +159,9 @@ const Page: React.FC = () => {
|
||||
|
||||
<IonToast
|
||||
position="top"
|
||||
color='danger'
|
||||
color="danger"
|
||||
isOpen={showToast}
|
||||
onDidDismiss={() => setShowToast(false)}
|
||||
onDidDismiss={() => closeToast(setShowToast)}
|
||||
message={messageToast}
|
||||
duration={2500}
|
||||
/>
|
||||
|
||||
@@ -16,11 +16,12 @@ import {
|
||||
} from "@ionic/react";
|
||||
import { add, locateOutline, locationOutline } from "ionicons/icons";
|
||||
import { useState } from "react";
|
||||
import { PageHeader } from "../../components/PageHeader";
|
||||
import "./MeusItinerarios.css";
|
||||
|
||||
interface ItineraryInfo {
|
||||
id_itinerary: number;
|
||||
van_plate: string;
|
||||
vehicle_plate: string;
|
||||
days_of_week: number;
|
||||
specific_day: string;
|
||||
estimated_departure_time: string;
|
||||
@@ -35,7 +36,7 @@ export default function MeusItinerarios() {
|
||||
[
|
||||
{
|
||||
id_itinerary: 1,
|
||||
van_plate: 'FSS1918',
|
||||
vehicle_plate: 'FSS1918',
|
||||
days_of_week: 3,
|
||||
specific_day: '24/08/2022',
|
||||
estimated_departure_time: '10:00',
|
||||
@@ -46,7 +47,7 @@ export default function MeusItinerarios() {
|
||||
},
|
||||
{
|
||||
id_itinerary: 1,
|
||||
van_plate: 'FSS1918',
|
||||
vehicle_plate: 'FSS1918',
|
||||
days_of_week: 3,
|
||||
specific_day: '24/08/2022',
|
||||
estimated_departure_time: '10:00',
|
||||
@@ -57,7 +58,7 @@ export default function MeusItinerarios() {
|
||||
},
|
||||
{
|
||||
id_itinerary: 1,
|
||||
van_plate: 'FSS1918',
|
||||
vehicle_plate: 'FSS1918',
|
||||
days_of_week: 3,
|
||||
specific_day: '24/08/2022',
|
||||
estimated_departure_time: '10:00',
|
||||
@@ -68,7 +69,7 @@ export default function MeusItinerarios() {
|
||||
},
|
||||
{
|
||||
id_itinerary: 1,
|
||||
van_plate: 'FSS1918',
|
||||
vehicle_plate: 'FSS1918',
|
||||
days_of_week: 3,
|
||||
specific_day: '24/08/2022',
|
||||
estimated_departure_time: '10:00',
|
||||
@@ -79,7 +80,7 @@ export default function MeusItinerarios() {
|
||||
},
|
||||
{
|
||||
id_itinerary: 1,
|
||||
van_plate: 'FSS1918',
|
||||
vehicle_plate: 'FSS1918',
|
||||
days_of_week: 3,
|
||||
specific_day: '24/08/2022',
|
||||
estimated_departure_time: '10:00',
|
||||
@@ -93,20 +94,12 @@ export default function MeusItinerarios() {
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonHeader translucent>
|
||||
<IonToolbar>
|
||||
<IonTitle>Meus Itinerários</IonTitle>
|
||||
<IonButtons slot="start">
|
||||
<IonBackButton text={""} defaultHref="/perfil" />
|
||||
</IonButtons>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
<PageHeader
|
||||
pageName="Meus Itinerários"
|
||||
backButtonPageUrl="/perfil"
|
||||
></PageHeader>
|
||||
|
||||
<IonContent fullscreen>
|
||||
<IonHeader collapse="condense">
|
||||
<IonToolbar>
|
||||
<IonTitle size="large">Meus Itinerários</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
{routes ? (
|
||||
routes.map((itinerary, index) => {
|
||||
return (
|
||||
|
||||
153
src/pages/MeusVeiculos.tsx
Normal file
153
src/pages/MeusVeiculos.tsx
Normal file
@@ -0,0 +1,153 @@
|
||||
import {
|
||||
IonBackButton,
|
||||
IonButtons,
|
||||
IonCard,
|
||||
IonCardContent,
|
||||
IonCardHeader,
|
||||
IonCardSubtitle,
|
||||
IonCardTitle,
|
||||
IonContent,
|
||||
IonHeader,
|
||||
IonIcon,
|
||||
IonItem,
|
||||
IonLabel,
|
||||
IonPage,
|
||||
IonTitle,
|
||||
IonToast,
|
||||
IonToolbar,
|
||||
} from "@ionic/react";
|
||||
import { Color } from "@ionic/core";
|
||||
import { carOutline, informationCircleOutline, peopleOutline } from "ionicons/icons";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import { useHistory, useLocation } from "react-router";
|
||||
|
||||
import { UserContext } from "../App";
|
||||
|
||||
import * as vehiclesRoutes from "../services/api/vehicles";
|
||||
|
||||
import sessionsService from "../services/functions/sessionsService";
|
||||
import { closeToast } from "../services/utils";
|
||||
import { PageHeader } from "../components/PageHeader";
|
||||
|
||||
interface VehicleInfo {
|
||||
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 MeusVeiculos: React.FC = () => {
|
||||
const history = useHistory();
|
||||
|
||||
const [showToast, setShowToast] = useState(false);
|
||||
const [toastMessage, setToastMessage] = useState("");
|
||||
const [toastColor, setToastColor] = useState<Color>("primary");
|
||||
|
||||
const [vehiclesList, setVehiclesList] = useState<VehicleInfo[]>();
|
||||
|
||||
const redirectUserToLogin = () => {
|
||||
history.push({ pathname: "/login" });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const getUserVehiclesList = async () => {
|
||||
let userId = "";
|
||||
|
||||
const refreshSessionRes = await sessionsService.refreshSession();
|
||||
|
||||
if (refreshSessionRes.error) {
|
||||
redirectUserToLogin();
|
||||
return;
|
||||
}
|
||||
|
||||
if (refreshSessionRes.userId) {
|
||||
userId = refreshSessionRes.userId;
|
||||
}
|
||||
|
||||
vehiclesRoutes
|
||||
.getByUserId(userId)
|
||||
.then((response) => {
|
||||
if (response.status === "error") {
|
||||
setToastColor("danger");
|
||||
setToastMessage(response.message);
|
||||
setShowToast(true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setVehiclesList(response.data);
|
||||
})
|
||||
.catch((err) => {
|
||||
setToastColor("danger");
|
||||
setToastMessage(err);
|
||||
setShowToast(true);
|
||||
});
|
||||
};
|
||||
|
||||
getUserVehiclesList();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<PageHeader
|
||||
pageName="Meus veículos"
|
||||
backButtonPageUrl="/perfil"
|
||||
></PageHeader>
|
||||
|
||||
<IonContent>
|
||||
{vehiclesList ? (
|
||||
<>
|
||||
<IonCard color={"primary"}>
|
||||
<IonCardContent>
|
||||
<IonIcon icon={informationCircleOutline} /> Toque em um veículo cadastrado para ver suas viagens e itinerários!
|
||||
</IonCardContent>
|
||||
</IonCard>
|
||||
{vehiclesList.map((vehicle, index) => {
|
||||
return (
|
||||
<IonCard button key={index}>
|
||||
<img src="https://s2.glbimg.com/-xUhYluyWnnnib57vy3QI1kD9oQ=/1200x/smart/filters:cover():strip_icc()/i.s3.glbimg.com/v1/AUTH_cf9d035bf26b4646b105bd958f32089d/internal_photos/bs/2020/y/E/vdU7J0TeAIC2kZONmgBQ/2018-09-04-sprintervanfoto.jpg" />
|
||||
<IonCardHeader>
|
||||
<IonCardTitle>
|
||||
{vehicle.brand} {vehicle.model}
|
||||
</IonCardTitle>
|
||||
<IonCardSubtitle>Placa: {vehicle.plate}</IonCardSubtitle>
|
||||
</IonCardHeader>
|
||||
<>
|
||||
<IonCardContent>
|
||||
<IonIcon icon={peopleOutline} size={"small"} />{" "}
|
||||
{vehicle.seats_number} assentos -{" "}
|
||||
{vehicle.locator_name ? (
|
||||
<>Locador: {vehicle.locator_name}</>
|
||||
) : (
|
||||
<>Não é alugado</>
|
||||
)}
|
||||
</IonCardContent>
|
||||
</>
|
||||
</IonCard>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
<IonToast
|
||||
position="top"
|
||||
color={toastColor}
|
||||
isOpen={showToast}
|
||||
onDidDismiss={() => closeToast(setShowToast)}
|
||||
message={toastMessage}
|
||||
duration={2500}
|
||||
/>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default MeusVeiculos;
|
||||
@@ -1,118 +0,0 @@
|
||||
import { IonBackButton, IonButtons, IonCard, IonCardContent, IonCardHeader, IonCardSubtitle, IonCardTitle, IonContent, IonHeader, IonIcon, IonItem, IonLabel, IonPage, IonTitle, IonToast, IonToolbar } from '@ionic/react';
|
||||
import { Color } from '@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;
|
||||
@@ -40,6 +40,8 @@ import sessionsService from "../services/functions/sessionsService";
|
||||
import usersService from "../services/functions/usersService";
|
||||
import { UserContext } from "../App";
|
||||
import { Color } from "@ionic/core";
|
||||
import { closeToast } from "../services/utils";
|
||||
import { PageHeader } from "../components/PageHeader";
|
||||
|
||||
interface ScanNewProps {
|
||||
match: {
|
||||
@@ -146,7 +148,7 @@ const Perfil: React.FC<ScanNewProps> = (props) => {
|
||||
return;
|
||||
}
|
||||
|
||||
// check if user is driver (if they have vans)
|
||||
// check if user is driver (if they have vehicles)
|
||||
const userIsDriverRes = await usersService.checkIfUserIsDriver(userId);
|
||||
|
||||
// if (userIsDriverRes.error) {
|
||||
@@ -211,22 +213,9 @@ const Perfil: React.FC<ScanNewProps> = (props) => {
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonHeader translucent>
|
||||
<IonToolbar>
|
||||
<IonTitle>Seu perfil</IonTitle>
|
||||
<IonButtons slot="start">
|
||||
<IonBackButton text="" defaultHref="/home" />
|
||||
</IonButtons>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
<PageHeader pageName="Meu perfil"></PageHeader>
|
||||
|
||||
<IonContent fullscreen>
|
||||
<IonHeader collapse="condense">
|
||||
<IonToolbar>
|
||||
<IonTitle size="large">Seu perfil</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonCard>
|
||||
<IonCardContent>
|
||||
<img
|
||||
@@ -334,20 +323,20 @@ const Perfil: React.FC<ScanNewProps> = (props) => {
|
||||
|
||||
<IonItem
|
||||
button
|
||||
onClick={() => history.push({ pathname: "/cadastro-van" })}
|
||||
onClick={() => history.push({ pathname: "/veiculos/cadastrar" })}
|
||||
>
|
||||
<IonIcon icon={carOutline} slot="start" />
|
||||
<IonLabel>Cadastrar van</IonLabel>
|
||||
<IonLabel>Cadastrar veículo</IonLabel>
|
||||
</IonItem>
|
||||
|
||||
{isDriver ? (
|
||||
<>
|
||||
<IonItem
|
||||
button
|
||||
onClick={() => history.push({ pathname: "/minhas-vans" })}
|
||||
onClick={() => history.push({ pathname: "/veiculos/meus" })}
|
||||
>
|
||||
<IonIcon icon={carOutline} slot="start" />
|
||||
<IonLabel>Minhas vans</IonLabel>
|
||||
<IonLabel>Meus veículos</IonLabel>
|
||||
</IonItem>
|
||||
<IonItem
|
||||
button
|
||||
@@ -393,7 +382,7 @@ const Perfil: React.FC<ScanNewProps> = (props) => {
|
||||
position="top"
|
||||
color={toastColor}
|
||||
isOpen={showToast}
|
||||
onDidDismiss={() => setShowToast(false)}
|
||||
onDidDismiss={() => closeToast(setShowToast)}
|
||||
message={toastMessage}
|
||||
duration={2500}
|
||||
/>
|
||||
|
||||
@@ -14,21 +14,23 @@ import {
|
||||
IonTextarea,
|
||||
IonTitle,
|
||||
IonToast,
|
||||
IonToolbar
|
||||
IonToolbar,
|
||||
} from "@ionic/react";
|
||||
import React, { useEffect, useReducer, useState } from "react";
|
||||
import { IonRow, IonCol } from "@ionic/react";
|
||||
|
||||
import './Perfil.css'
|
||||
import "./Perfil.css";
|
||||
import { useHistory, useLocation } from "react-router";
|
||||
import { saveOutline } from "ionicons/icons";
|
||||
|
||||
import isEqual from 'lodash.isequal';
|
||||
import isEqual from "lodash.isequal";
|
||||
|
||||
import * as usersRoutes from '../services/api/users';
|
||||
import * as usersRoutes from "../services/api/users";
|
||||
|
||||
import './Cadastro/Cadastro.css'
|
||||
import "./Cadastro/Cadastro.css";
|
||||
import { Color } from "@ionic/core";
|
||||
import { closeToast } from "../services/utils";
|
||||
import { PageHeader } from "../components/PageHeader";
|
||||
|
||||
interface userData {
|
||||
name: string;
|
||||
@@ -39,7 +41,7 @@ interface userData {
|
||||
}
|
||||
|
||||
interface LocationState {
|
||||
userData: userData
|
||||
userData: userData;
|
||||
}
|
||||
|
||||
const PerfilEditar: React.FC = () => {
|
||||
@@ -47,97 +49,99 @@ const PerfilEditar: React.FC = () => {
|
||||
const location = useLocation<LocationState>();
|
||||
|
||||
const [showToast, setShowToast] = useState(false);
|
||||
const [messageToast, setMessageToast] = useState('');
|
||||
const [messageToast, setMessageToast] = useState("");
|
||||
const [toastColor, setToastColor] = useState<Color>("primary");
|
||||
|
||||
const [userData, setUserData] = useState({
|
||||
name: '',
|
||||
lastname: '',
|
||||
email: '',
|
||||
birth_date: '',
|
||||
bio: '',
|
||||
name: "",
|
||||
lastname: "",
|
||||
email: "",
|
||||
birth_date: "",
|
||||
bio: "",
|
||||
});
|
||||
|
||||
const [inputValues, setInputValues] = useReducer(
|
||||
(state: any, newState: any) => ({ ...state, ...newState }),
|
||||
{
|
||||
name: '',
|
||||
lastname: '',
|
||||
email: '',
|
||||
birth_date: '',
|
||||
bio: '',
|
||||
name: "",
|
||||
lastname: "",
|
||||
email: "",
|
||||
birth_date: "",
|
||||
bio: "",
|
||||
}
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!location.state) {
|
||||
history.push({ pathname: '/perfil' })
|
||||
history.push({ pathname: "/perfil" });
|
||||
}
|
||||
|
||||
let userData = location.state.userData
|
||||
let userData = location.state.userData;
|
||||
|
||||
setUserData(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
|
||||
name: userData.name,
|
||||
lastname: userData.lastname,
|
||||
email: userData.email,
|
||||
birth_date: userData.birth_date,
|
||||
bio: userData.bio,
|
||||
});
|
||||
|
||||
console.log(inputValues)
|
||||
}, [userData]);
|
||||
|
||||
const handleUpdateUserData = () => {
|
||||
usersRoutes.update(inputValues).then(response => {
|
||||
if (response.status === 'error') {
|
||||
setToastColor("danger")
|
||||
setMessageToast(response.message);
|
||||
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);
|
||||
|
||||
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 isEqual(userData, inputValues);
|
||||
};
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonHeader>
|
||||
<IonToolbar>
|
||||
<IonTitle>Editar perfil</IonTitle>
|
||||
<IonButtons slot="start">
|
||||
<IonBackButton defaultHref="/perfil" />
|
||||
</IonButtons>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
<PageHeader
|
||||
pageName="Editar perfil"
|
||||
backButtonPageUrl="/perfil"
|
||||
></PageHeader>
|
||||
|
||||
<IonContent>
|
||||
<IonGrid>
|
||||
<IonRow>
|
||||
<IonCol size="12">
|
||||
<div id='nome-sobrenome'>
|
||||
<div id="nome-sobrenome">
|
||||
<IonItem>
|
||||
<IonLabel position="stacked"> Nome</IonLabel>
|
||||
<IonInput
|
||||
type="text"
|
||||
value={inputValues.name}
|
||||
onIonChange={(e) => setInputValues({'name': e.detail.value!})}
|
||||
onIonChange={(e) =>
|
||||
setInputValues({ name: e.detail.value! })
|
||||
}
|
||||
></IonInput>
|
||||
</IonItem>
|
||||
|
||||
@@ -146,7 +150,9 @@ const PerfilEditar: React.FC = () => {
|
||||
<IonInput
|
||||
type="text"
|
||||
value={inputValues.lastname}
|
||||
onIonChange={(e) => setInputValues({'lastname': e.detail.value!})}
|
||||
onIonChange={(e) =>
|
||||
setInputValues({ lastname: e.detail.value! })
|
||||
}
|
||||
></IonInput>
|
||||
</IonItem>
|
||||
</div>
|
||||
@@ -156,25 +162,28 @@ const PerfilEditar: React.FC = () => {
|
||||
<IonInput
|
||||
type="email"
|
||||
value={inputValues.email}
|
||||
onIonChange={(e) => setInputValues({'email': e.detail.value!})}
|
||||
onIonChange={(e) =>
|
||||
setInputValues({ email: e.detail.value! })
|
||||
}
|
||||
></IonInput>
|
||||
</IonItem>
|
||||
|
||||
<IonItem>
|
||||
<IonLabel position='stacked'>Data de nascimento</IonLabel>
|
||||
<IonInput
|
||||
type='date'
|
||||
<IonLabel position="stacked">Data de nascimento</IonLabel>
|
||||
<IonInput
|
||||
type="date"
|
||||
value={inputValues.birth_date}
|
||||
onIonChange={(e) => setInputValues({'birth_date': e.detail.value!}) }
|
||||
>
|
||||
</IonInput>
|
||||
onIonChange={(e) =>
|
||||
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!})}
|
||||
onIonChange={(e) => setInputValues({ bio: e.detail.value! })}
|
||||
></IonTextarea>
|
||||
</IonItem>
|
||||
</IonCol>
|
||||
@@ -182,7 +191,10 @@ const PerfilEditar: React.FC = () => {
|
||||
</IonGrid>
|
||||
|
||||
<IonFab vertical="bottom" horizontal="end" slot="fixed">
|
||||
<IonFabButton disabled={hasChangedSinceInitialState()} onClick={handleUpdateUserData}>
|
||||
<IonFabButton
|
||||
disabled={hasChangedSinceInitialState()}
|
||||
onClick={handleUpdateUserData}
|
||||
>
|
||||
<IonIcon icon={saveOutline} />
|
||||
</IonFabButton>
|
||||
</IonFab>
|
||||
@@ -190,7 +202,7 @@ const PerfilEditar: React.FC = () => {
|
||||
<IonToast
|
||||
color={toastColor}
|
||||
isOpen={showToast}
|
||||
onDidDismiss={() => setShowToast(false)}
|
||||
onDidDismiss={() => closeToast(setShowToast)}
|
||||
message={messageToast}
|
||||
duration={2500}
|
||||
/>
|
||||
|
||||
@@ -45,9 +45,10 @@ import {
|
||||
} from "ionicons/icons";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useHistory, useLocation } from "react-router";
|
||||
import { getTransportes } from "../../services/functions/transportsService";
|
||||
import itinerariesService from "../../services/functions/itinerariesService";
|
||||
import { createUserSearch } from "../../services/api/users";
|
||||
import "./Transportes.css";
|
||||
import { closeToast } from "../../services/utils";
|
||||
|
||||
interface InfoBusca {
|
||||
addressFrom: any;
|
||||
@@ -60,7 +61,7 @@ const Transportes: React.FC = () => {
|
||||
const history = useHistory();
|
||||
const location = useLocation();
|
||||
const props = location.state as InfoBusca;
|
||||
const [transportes, setTransportes] = useState([]);
|
||||
const [itinerarios, setItinerarios] = useState([]);
|
||||
const [showModalFilters, setShowModalFilters] = useState(false);
|
||||
const [showToast, setShowToast] = useState(false);
|
||||
const [messageToast, setMessageToast ] = useState('');
|
||||
@@ -68,13 +69,13 @@ const Transportes: React.FC = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (props) {
|
||||
buscaTransportes();
|
||||
buscaItinerarios();
|
||||
}
|
||||
}, [props]);
|
||||
|
||||
async function buscaTransportes() {
|
||||
let data = (await getTransportes(props)) as any;
|
||||
setTransportes(data);
|
||||
async function buscaItinerarios() {
|
||||
let data = (await itinerariesService.searchItineraries(props)) as any;
|
||||
setItinerarios(data);
|
||||
}
|
||||
|
||||
function criaAlerta(){
|
||||
@@ -90,6 +91,7 @@ const Transportes: React.FC = () => {
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
{/* TODO, componentizar Header */}
|
||||
<IonHeader>
|
||||
<div className="header-page">
|
||||
{/* <IonButtons slot="start">
|
||||
@@ -107,7 +109,7 @@ const Transportes: React.FC = () => {
|
||||
</div>
|
||||
</IonHeader>
|
||||
<IonContent fullscreen>
|
||||
{transportes && transportes.length > 0? (
|
||||
{itinerarios && itinerarios.length > 0? (
|
||||
<div className="header-tabs">
|
||||
<IonSlides>
|
||||
<IonSlide>
|
||||
@@ -127,8 +129,8 @@ const Transportes: React.FC = () => {
|
||||
)
|
||||
:
|
||||
(<h1 className="msg-not-found">Não foi encontrado nenhum transporte que atenda essa rota.</h1>)}
|
||||
{transportes &&
|
||||
transportes.map((record: any, index: any) => {
|
||||
{itinerarios &&
|
||||
itinerarios.map((record: any, index: any) => {
|
||||
return (
|
||||
<IonCard className="card-transporte" key={index}>
|
||||
<IonCardContent>
|
||||
@@ -210,7 +212,7 @@ const Transportes: React.FC = () => {
|
||||
// cssClass={"toast-notification"}
|
||||
color={toastColor}
|
||||
isOpen={showToast}
|
||||
onDidDismiss={() => setShowToast(false)}
|
||||
onDidDismiss={() => closeToast(setShowToast)}
|
||||
message={messageToast}
|
||||
duration={2500}
|
||||
/>
|
||||
|
||||
423
src/pages/VeiculoCadastro.tsx
Normal file
423
src/pages/VeiculoCadastro.tsx
Normal file
@@ -0,0 +1,423 @@
|
||||
import {
|
||||
IonToast,
|
||||
IonItem,
|
||||
IonLabel,
|
||||
IonInput,
|
||||
IonBackButton,
|
||||
IonButton,
|
||||
IonButtons,
|
||||
IonContent,
|
||||
IonHeader,
|
||||
IonPage,
|
||||
IonToolbar,
|
||||
IonTitle,
|
||||
IonList,
|
||||
IonCheckbox,
|
||||
IonListHeader,
|
||||
IonSelect,
|
||||
IonSelectOption,
|
||||
IonItemDivider,
|
||||
} 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 vehiclesRoutes from "../services/api/vehicles";
|
||||
|
||||
import "./VeiculoCadastro.css";
|
||||
import { Color } from "@ionic/core";
|
||||
import { closeToast } from "../services/utils";
|
||||
import { PageHeader } from "../components/PageHeader";
|
||||
|
||||
const VeiculoCadastro: React.FC = () => {
|
||||
const history = useHistory();
|
||||
|
||||
const [showToast, setShowToast] = useState<boolean>(false);
|
||||
const [toastMessage, setToastMessage] = useState<string>("");
|
||||
const [toastColor, setToastColor] = useState<Color>("primary");
|
||||
|
||||
const [carBrands, setCarBrands] = useState([
|
||||
{
|
||||
codigo: "",
|
||||
nome: "",
|
||||
},
|
||||
]);
|
||||
|
||||
const [carModels, setCarModels] = useState([
|
||||
{
|
||||
codigo: "",
|
||||
nome: "",
|
||||
},
|
||||
]);
|
||||
|
||||
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 vehicleForm = {
|
||||
carPlate: inputValues.carPlate,
|
||||
carBrand: inputValues.carBrand,
|
||||
carModel: inputValues.carModel,
|
||||
seats_number: inputValues.seats_number,
|
||||
isRented: inputValues.isRented,
|
||||
};
|
||||
|
||||
if (
|
||||
!vehicleForm.carPlate ||
|
||||
vehicleForm.carPlate.length !== 7 ||
|
||||
!vehicleForm.carPlate.match(/([A-z0-9]){7}/g)
|
||||
) {
|
||||
setToastMessage("Placa do veículo inválida!");
|
||||
setShowToast(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!vehicleForm.carBrand) {
|
||||
setToastMessage("Marca do veículo é obrigatório");
|
||||
setShowToast(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!vehicleForm.carModel) {
|
||||
setToastMessage("Modelo do veículo é obrigatório");
|
||||
setShowToast(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!vehicleForm.seats_number || !parseInt(`${vehicleForm.seats_number}`)) {
|
||||
setToastMessage("Número de passageiros inválido");
|
||||
setShowToast(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Number(vehicleForm.seats_number) < 1) {
|
||||
setToastMessage("Número de passageiros deve ser positivo!");
|
||||
setShowToast(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (vehicleForm.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 loadCarModels = async (carBrandId: string) => {
|
||||
const carModelsRes = await carsService.getCarModels(carBrandId);
|
||||
|
||||
if (carModelsRes.error) {
|
||||
setToastColor("danger");
|
||||
setToastMessage(carModelsRes.error.errorMessage);
|
||||
setInputValues({ carBrand: "" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (carModelsRes.data) {
|
||||
setCarModels(carModelsRes.data);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!validateForm()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// cria registro da vehicle
|
||||
await vehiclesRoutes
|
||||
.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: "/minhas-vehicles",
|
||||
state: {
|
||||
redirectData: {
|
||||
showToastMessage: true,
|
||||
toastColor: "success",
|
||||
toastMessage: response.message,
|
||||
},
|
||||
},
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
setToastColor("danger");
|
||||
setToastMessage(err);
|
||||
setShowToast(true);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
let isMounted = true;
|
||||
|
||||
const getCarsBrands = async () => {
|
||||
const carBrandsRes = await carsService.getAllCarBrands();
|
||||
|
||||
if (carBrandsRes.error) {
|
||||
setToastColor("danger");
|
||||
setToastMessage(carBrandsRes.error.errorMessage);
|
||||
setShowToast(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (carBrandsRes.data) {
|
||||
if (isMounted) {
|
||||
setCarBrands(carBrandsRes.data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
getCarsBrands();
|
||||
|
||||
return () => {
|
||||
isMounted = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<PageHeader
|
||||
pageName="Cadastro de veículo"
|
||||
backButtonPageUrl="/perfil"
|
||||
></PageHeader>
|
||||
|
||||
<IonContent>
|
||||
<IonList lines="full" class="ion-no-margin">
|
||||
<IonItemDivider color={"primary"}>Informações do veículo</IonItemDivider>
|
||||
<IonItem>
|
||||
<IonLabel position="fixed">Placa </IonLabel>
|
||||
<IonInput
|
||||
type="text"
|
||||
clearInput
|
||||
maxlength={7}
|
||||
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 });
|
||||
loadCarModels(e.detail.value);
|
||||
}}
|
||||
>
|
||||
{carBrands ? (
|
||||
carBrands.map((carBrand, index) => {
|
||||
return (
|
||||
<IonSelectOption key={index} value={index}>
|
||||
{carBrand.nome}
|
||||
</IonSelectOption>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</IonSelect>
|
||||
</IonItem>
|
||||
|
||||
{inputValues.carBrand ? (
|
||||
<IonItem>
|
||||
<IonLabel>Modelo</IonLabel>
|
||||
<IonSelect
|
||||
onIonChange={(e: any) => {
|
||||
setInputValues({ carModel: e.detail.value });
|
||||
}}
|
||||
>
|
||||
{carModels ? (
|
||||
carModels.map((carModel, index) => {
|
||||
return (
|
||||
<IonSelectOption key={index} value={carModel.nome}>
|
||||
{carModel.nome}
|
||||
</IonSelectOption>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</IonSelect>
|
||||
</IonItem>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
<IonItem>
|
||||
<IonLabel position="fixed">Nº assentos</IonLabel>
|
||||
<IonInput
|
||||
type="number"
|
||||
min={1}
|
||||
clearInput
|
||||
onIonChange={(e: any) =>
|
||||
setInputValues({ seats_number: e.target.value })
|
||||
}
|
||||
/>
|
||||
</IonItem>
|
||||
</IonList>
|
||||
|
||||
<IonItemDivider color={"medium"}>Informações de locação</IonItemDivider>
|
||||
|
||||
<IonList lines="full" class="ion-no-margin">
|
||||
<IonItem>
|
||||
<IonLabel>O veículo é alugado?</IonLabel>
|
||||
<IonCheckbox
|
||||
checked={inputValues.isRented}
|
||||
onIonChange={(e) =>
|
||||
setInputValues({ isRented: e.detail.checked })
|
||||
}
|
||||
/>
|
||||
</IonItem>
|
||||
|
||||
{inputValues.isRented && (
|
||||
<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>
|
||||
<IonButton
|
||||
className="ion-margin-top"
|
||||
expand="block"
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
Salvar
|
||||
</IonButton>
|
||||
</div>
|
||||
</IonList>
|
||||
|
||||
<IonToast
|
||||
position="top"
|
||||
color={toastColor}
|
||||
isOpen={showToast}
|
||||
onDidDismiss={() => closeToast(setShowToast)}
|
||||
message={toastMessage}
|
||||
duration={2500}
|
||||
/>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default VeiculoCadastro;
|
||||
39
src/services/api/itineraries.ts
Normal file
39
src/services/api/itineraries.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import instance from './api';
|
||||
// import LocalStorage from '../LocalStorage';
|
||||
|
||||
import transportsRoutes from '../../constants/routes/itinerariesRoutes';
|
||||
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 Coordinates {
|
||||
lat: number,
|
||||
lng: number
|
||||
}
|
||||
|
||||
export async function get() {
|
||||
updateHeader();
|
||||
|
||||
const response = await instance.get(transportsRoutes.get.url, { headers: header });
|
||||
return response.data;
|
||||
}
|
||||
|
||||
export async function search(coordinatesOrigin: Coordinates, coordinatesDestination: Coordinates) {
|
||||
updateHeader();
|
||||
|
||||
const response = await instance.post(transportsRoutes.search.url, { coordinatesOrigin, coordinatesDestination }, { headers: header });
|
||||
return response.data;
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
import instance from './api';
|
||||
// import LocalStorage from '../LocalStorage';
|
||||
|
||||
import transportsRoutes from '../../constants/routes/transportsRoutes';
|
||||
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 getTransportsRequest {
|
||||
coordinatesFrom: {
|
||||
lat: number,
|
||||
lng: number
|
||||
},
|
||||
coordinatesTo: {
|
||||
lat: number,
|
||||
lng: number
|
||||
}
|
||||
}
|
||||
|
||||
export async function get(coordinates: getTransportsRequest) {
|
||||
updateHeader();
|
||||
|
||||
const response = await instance.get(transportsRoutes.get.url + `/${coordinates}`, { headers: header });
|
||||
return response.data;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import instance from "./api";
|
||||
|
||||
import vansRoutes from "../../constants/routes/vansRoutes";
|
||||
import vehiclesRoutes from "../../constants/routes/vehiclesRoutes";
|
||||
import { AxiosRequestHeaders } from "axios";
|
||||
import LocalStorage from "../../LocalStorage";
|
||||
|
||||
@@ -17,10 +17,10 @@ function updateHeader() {
|
||||
};
|
||||
}
|
||||
|
||||
export async function getByPlate(vanId: string) {
|
||||
export async function getByPlate(vehicleId: string) {
|
||||
updateHeader();
|
||||
|
||||
const response = await instance.get(vansRoutes.getByPlate.url + `/${vanId}`, {
|
||||
const response = await instance.get(vehiclesRoutes.getByPlate.url + `/${vehicleId}`, {
|
||||
headers: header,
|
||||
});
|
||||
|
||||
@@ -30,14 +30,14 @@ export async function getByPlate(vanId: string) {
|
||||
export async function getByUserId(userId: string) {
|
||||
updateHeader();
|
||||
|
||||
const response = await instance.get(vansRoutes.getByUserId.url + `/${userId}`, {
|
||||
const response = await instance.get(vehiclesRoutes.getByUserId.url + `/${userId}`, {
|
||||
headers: header,
|
||||
});
|
||||
|
||||
return response.data;
|
||||
}
|
||||
|
||||
interface CreateVanBody {
|
||||
interface CreateVehicleBody {
|
||||
plate: string;
|
||||
brand: string;
|
||||
model: string;
|
||||
@@ -49,23 +49,23 @@ interface CreateVanBody {
|
||||
locator_state: string;
|
||||
}
|
||||
|
||||
export async function create(CreateVanBody: CreateVanBody) {
|
||||
export async function create(CreateVehicleBody: CreateVehicleBody) {
|
||||
updateHeader();
|
||||
|
||||
const response = await instance.post(vansRoutes.create.url, CreateVanBody, { headers: header });
|
||||
const response = await instance.post(vehiclesRoutes.create.url, CreateVehicleBody, { headers: header });
|
||||
return response.data;
|
||||
}
|
||||
|
||||
interface UpdateVanBody {
|
||||
interface UpdateVehicleBody {
|
||||
brand?: string;
|
||||
model?: string;
|
||||
seats_number?: string;
|
||||
}
|
||||
|
||||
export async function update(vanData: UpdateVanBody) {
|
||||
export async function update(vehicleData: UpdateVehicleBody) {
|
||||
updateHeader();
|
||||
|
||||
const response = await instance.patch(vansRoutes.update.url, vanData, {
|
||||
const response = await instance.patch(vehiclesRoutes.update.url, vehicleData, {
|
||||
headers: header,
|
||||
});
|
||||
|
||||
38
src/services/functions/itinerariesService.ts
Normal file
38
src/services/functions/itinerariesService.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import * as itinerariesRoutes from '../api/itineraries';
|
||||
|
||||
interface CoordinatesRequest {
|
||||
coordinatesFrom: {
|
||||
lat: number,
|
||||
lng: number
|
||||
},
|
||||
coordinatesTo: {
|
||||
lat: number,
|
||||
lng: number
|
||||
}
|
||||
}
|
||||
|
||||
export async function getAllItineraries(): Promise<any> {
|
||||
let res: any;
|
||||
|
||||
try {
|
||||
res = await itinerariesRoutes.get();
|
||||
} catch (error) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
return res.data
|
||||
}
|
||||
|
||||
|
||||
export async function searchItineraries({ coordinatesFrom, coordinatesTo }: CoordinatesRequest): Promise<any> {
|
||||
let res: any
|
||||
try {
|
||||
res = await itinerariesRoutes.search(coordinatesFrom, coordinatesTo);
|
||||
} catch (error) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
return res.data
|
||||
}
|
||||
|
||||
export default { getAllItineraries, searchItineraries }
|
||||
@@ -1,20 +0,0 @@
|
||||
import * as transportsRoutes from '../api/transports';
|
||||
|
||||
interface CoordinatesRequest {
|
||||
coordinatesFrom:{
|
||||
lat: number,
|
||||
lng: number
|
||||
},
|
||||
coordinatesTo:{
|
||||
lat: number,
|
||||
lng: number
|
||||
}
|
||||
}
|
||||
|
||||
export async function getTransportes(request: CoordinatesRequest) : Promise<any> {
|
||||
try {
|
||||
let res : any = await transportsRoutes.get(request);
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -4,4 +4,9 @@ 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;
|
||||
}
|
||||
|
||||
export async function closeToast(setShowToast: React.Dispatch<React.SetStateAction<boolean>>) {
|
||||
setShowToast(false)
|
||||
window.history.replaceState({}, document.title)
|
||||
}
|
||||
4
src/theme/tailwind.css
Normal file
4
src/theme/tailwind.css
Normal file
@@ -0,0 +1,4 @@
|
||||
/* ./src/theme/tailwind.css */
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
12
tailwind.config.js
Normal file
12
tailwind.config.js
Normal file
@@ -0,0 +1,12 @@
|
||||
module.exports = {
|
||||
mode: 'jit',
|
||||
purge: ['./public/index.html', './src/**/*.{js,jsx,ts,tsx,css}'],
|
||||
darkMode: false, // or 'media' or 'class'
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
variants: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
Reference in New Issue
Block a user