Merge pull request #15 from Hzin/VAN-45-create-view-search-itinerary

Van 45 create view search itinerary
This commit is contained in:
CloudAlb
2022-09-09 21:14:41 -03:00
committed by GitHub
45 changed files with 32288 additions and 17230 deletions

11
craco.config.js Normal file
View File

@@ -0,0 +1,11 @@
// craco.config.js
module.exports = {
style: {
postcss: {
plugins: [
require('tailwindcss'),
require('autoprefixer'),
],
},
},
}

28547
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -8,6 +8,7 @@
"@capacitor/haptics": "1.1.4", "@capacitor/haptics": "1.1.4",
"@capacitor/keyboard": "1.2.2", "@capacitor/keyboard": "1.2.2",
"@capacitor/status-bar": "1.0.8", "@capacitor/status-bar": "1.0.8",
"@craco/craco": "^6.4.5",
"@hookform/error-message": "^2.0.0", "@hookform/error-message": "^2.0.0",
"@ionic-selectable/core": "^5.0.0-alpha.13", "@ionic-selectable/core": "^5.0.0-alpha.13",
"@ionic/react": "^6.0.0", "@ionic/react": "^6.0.0",
@@ -17,21 +18,23 @@
"@testing-library/user-event": "^12.6.3", "@testing-library/user-event": "^12.6.3",
"@types/jest": "^26.0.20", "@types/jest": "^26.0.20",
"@types/node": "^12.19.15", "@types/node": "^12.19.15",
"@types/react": "^16.14.3", "@types/react": "^18.0.18",
"@types/react-dom": "^16.9.10", "@types/react-dom": "^18.0.6",
"@types/react-router": "^5.1.11", "@types/react-router": "^5.1.11",
"@types/react-router-dom": "^5.1.7", "@types/react-router-dom": "^5.1.7",
"axios": "^0.26.1", "axios": "^0.26.1",
"cross-env": "^7.0.3",
"ionicons": "^5.4.0", "ionicons": "^5.4.0",
"lodash.isequal": "^4.5.0", "lodash.isequal": "^4.5.0",
"pigeon-maps": "^0.21.0", "pigeon-maps": "^0.21.0",
"pullstate": "^1.24.0", "pullstate": "^1.24.0",
"react": "^17.0.1", "react": "^18.2.0",
"react-dom": "^17.0.1", "react-dom": "^18.2.0",
"react-google-places-autocomplete": "^3.4.0", "react-google-places-autocomplete": "^3.4.0",
"react-hook-form": "^7.30.0", "react-hook-form": "^7.30.0",
"react-router": "^5.2.0", "react-router": "^5.2.0",
"react-router-dom": "^5.2.0", "react-router-dom": "^5.2.0",
"react-scripts": "4.0.3",
"reselect": "^4.1.5", "reselect": "^4.1.5",
"typescript": "^4.1.3", "typescript": "^4.1.3",
"web-vitals": "^0.2.4", "web-vitals": "^0.2.4",
@@ -50,8 +53,10 @@
"yup": "^0.32.11" "yup": "^0.32.11"
}, },
"scripts": { "scripts": {
"start": "react-scripts start", "start": "cross-env TAILWIND_MODE=watch craco start",
"build": "react-scripts build", "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)/)'", "test": "react-scripts test --transformIgnorePatterns 'node_modules/(?!(@ionic/react|@ionic/react-router|@ionic/core|@stencil/core|ionicons)/)'",
"eject": "react-scripts eject" "eject": "react-scripts eject"
}, },
@@ -77,7 +82,9 @@
"@capacitor/cli": "3.4.3", "@capacitor/cli": "3.4.3",
"@types/axios": "^0.14.0", "@types/axios": "^0.14.0",
"@types/lodash.isequal": "^4.5.6", "@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" "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
View File

@@ -0,0 +1,7 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

View File

@@ -1,4 +1,4 @@
import { Redirect, Route } from 'react-router-dom'; import React, { useContext, useState } from "react";
import { import {
IonApp, IonApp,
IonIcon, IonIcon,
@@ -7,109 +7,125 @@ import {
IonTabBar, IonTabBar,
IonTabButton, IonTabButton,
IonTabs, IonTabs,
setupIonicReact setupIonicReact,
} from '@ionic/react'; } from "@ionic/react";
import { IonReactRouter } from '@ionic/react-router'; 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 // importação das páginas
import Cadastro from './pages/Cadastro/Cadastro'; import Cadastro from "./pages/Cadastro/Cadastro";
import Login from './pages/Login'; import CadastroCompletar from "./pages/CadastroCompletar/CadastroCompletar";
import Home from './pages/Home'; import CompletarDocumento from "./pages/CadastroCompletar/CompletarDocumento";
import Perfil from './pages/Perfil'; import CompletarTelefone from "./pages/CadastroCompletar/CompletarTelefone";
import PerfilEditar from './pages/PerfilEditar';
import CadastroVan from './pages/CadastroVan'; import Home from "./pages/Home";
import CadastroCompletar from './pages/CadastroCompletar/CadastroCompletar'; import Login from "./pages/Login";
import CompletarDocumento from './pages/CadastroCompletar/CompletarDocumento'; import Perfil from "./pages/Perfil";
import CompletarTelefone from './pages/CadastroCompletar/CompletarTelefone'; import PerfilEditar from "./pages/PerfilEditar";
import Transportes from './pages/Transportes/Transportes';
import BuscarPassageiro from './pages/BuscarPassageiro/BuscarPassageiro'; import VeiculoCadastro from "./pages/VeiculoCadastro";
import BuscarTransporte from './pages/BuscarTransporte/BuscarTransporte'; import MeusVeiculos from "./pages/MeusVeiculos";
import Debug from './pages/Debug'; 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 */ /* 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 */ /* Basic CSS for apps built with Ionic */
import '@ionic/react/css/normalize.css'; import "@ionic/react/css/normalize.css";
import '@ionic/react/css/structure.css'; import "@ionic/react/css/structure.css";
import '@ionic/react/css/typography.css'; import "@ionic/react/css/typography.css";
/* Optional CSS utils that can be commented out */ /* Optional CSS utils that can be commented out */
import '@ionic/react/css/padding.css'; import "@ionic/react/css/display.css";
import '@ionic/react/css/float-elements.css'; import "@ionic/react/css/flex-utils.css";
import '@ionic/react/css/text-alignment.css'; import "@ionic/react/css/float-elements.css";
import '@ionic/react/css/text-transformation.css'; import "@ionic/react/css/padding.css";
import '@ionic/react/css/flex-utils.css'; import "@ionic/react/css/text-alignment.css";
import '@ionic/react/css/display.css'; import "@ionic/react/css/text-transformation.css";
/* Theme variables */ /* Theme variables */
import './theme/variables.css'; import "./theme/variables.css";
// import Tabs from './components/Tabs'; /* Tailwind styles */
import { search, home, person } from 'ionicons/icons'; import "./theme/tailwind.css";
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';
setupIonicReact(); setupIonicReact();
const routes = ( const routes = (
<> <>
<Route exact path="/cadastro" component={Cadastro}></Route> <Route exact path="/cadastro" component={Cadastro}></Route>
<Route exact path="/login" component={Login}></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/editar" component={PerfilEditar}></Route>
<Route exact path="/perfil/completar" component={CadastroCompletar}></Route>
<Route exact path="/perfil/completar/documento" component={CompletarDocumento}></Route>
<Route exact path="/perfil/completar/telefone" component={CompletarTelefone}></Route>
<Route exact path="/transportes" component={Transportes}></Route> <Route exact path="/veiculos/cadastrar" component={VeiculoCadastro}></Route>
<Route exact path="/buscar-passageiro" component={BuscarPassageiro}></Route> <Route exact path="/veiculos/meus" component={MeusVeiculos}></Route>
<Route exact path="/buscar-transporte" component={BuscarTransporte}></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="/">
<Route exact path="/minhas-vans" component={MinhasVans}></Route> <Redirect to="/home" />
</Route>
<Route exact path="/cadastrar-itinerario" component={CadastrarItinerario}></Route> </>
<Route exact path="/meus-itinerarios" component={MeusItinerarios}></Route> );
<Route exact path="/">
<Redirect to="/login" />
</Route>
</>)
interface IUserManager { interface IUserManager {
setIsLoggedIn: Function; setIsLoggedIn: Function;
} }
const user: IUserManager = { const user: IUserManager = {
setIsLoggedIn: () => {} setIsLoggedIn: () => {},
}; };
export const UserContext = React.createContext<IUserManager>(user); export const UserContext = React.createContext<IUserManager>(user);
const IonicApp: React.FC = () => { const IonicApp: React.FC = () => {
const [isLoggedIn, setIsLoggedIn] = useState(false); const [isLoggedIn, setIsLoggedIn] = useState(false);
const user = useContext(UserContext); const user = useContext(UserContext);
user.setIsLoggedIn = setIsLoggedIn; user.setIsLoggedIn = setIsLoggedIn;
return( return (
<IonApp> <IonApp>
<IonReactRouter> <IonReactRouter>
{isLoggedIn ? ( {isLoggedIn ? (
<IonTabs> <IonTabs>
<IonRouterOutlet>{routes}</IonRouterOutlet> <IonRouterOutlet>{routes}</IonRouterOutlet>
<IonTabBar slot="bottom"> <IonTabBar slot="bottom">
<IonTabButton tab="buscar" href="/buscar-transporte"> <IonTabButton tab="buscar" href="/buscas">
<IonIcon icon={search} /> <IonIcon icon={search} />
<IonLabel>Buscar</IonLabel> <IonLabel>Buscar</IonLabel>
</IonTabButton> </IonTabButton>
@@ -123,11 +139,13 @@ const IonicApp: React.FC = () => {
</IonTabButton> </IonTabButton>
</IonTabBar> </IonTabBar>
</IonTabs> </IonTabs>
) : (<IonRouterOutlet>{routes}</IonRouterOutlet>)} ) : (
<IonRouterOutlet>{routes}</IonRouterOutlet>
)}
</IonReactRouter> </IonReactRouter>
</IonApp> </IonApp>
) );
} };
const App: React.FC = () => { const App: React.FC = () => {
return ( return (

View File

@@ -1,17 +1,19 @@
import { IonCol, IonRouterLink, IonRow } from "@ionic/react"; import { IonCol, IonRouterLink, IonRow } from "@ionic/react";
interface ComponentProps { interface ComponentProps {
message: string, message: string;
link: string, link: string;
text: string text: string;
} }
export const Action = (props: ComponentProps) => ( export const Action = (props: ComponentProps) => (
<IonRow className="ion-text-center ion-justify-content-center">
<IonRow className="ion-text-center ion-justify-content-center"> <IonCol size="12">
<IonCol size="12"> {props.message}
{ props.message } <IonRouterLink className="custom-link" routerLink={props.link}>
<IonRouterLink className="custom-link" routerLink={ props.link }> { props.text } &rarr;</IonRouterLink> {" "}
</IonCol> {props.text} &rarr;
</IonRow> </IonRouterLink>
); </IonCol>
</IonRow>
);

View 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>
);

View File

@@ -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

View File

@@ -1,4 +1,4 @@
const transportsRoutesDefault = '/transports'; const transportsRoutesDefault = '/itineraries';
const transportsRoutes = { const transportsRoutes = {
create: { create: {
url: `${transportsRoutesDefault}` url: `${transportsRoutesDefault}`
@@ -6,6 +6,9 @@ const transportsRoutes = {
get: { get: {
url: `${transportsRoutesDefault}` url: `${transportsRoutesDefault}`
}, },
search: {
url: `${transportsRoutesDefault}/search/inradius`
},
update: { update: {
url: `${transportsRoutesDefault}` url: `${transportsRoutesDefault}`
}, },

View File

@@ -1,17 +1,17 @@
const vansRoutesDefault = '/vans/locator'; const vehiclesRoutesDefault = '/vehicles/locator';
const vansRoutes = { const vehiclesRoutes = {
list: { list: {
url: `${vansRoutesDefault}/list` url: `${vehiclesRoutesDefault}/list`
}, },
getById: { getById: {
url: `${vansRoutesDefault}/` url: `${vehiclesRoutesDefault}/`
}, },
create: { create: {
url: `${vansRoutesDefault}/` url: `${vehiclesRoutesDefault}/`
}, },
update: { update: {
url: `${vansRoutesDefault}/edit` url: `${vehiclesRoutesDefault}/edit`
} }
} }
export default vansRoutes; export default vehiclesRoutes;

View File

@@ -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;

View 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;

View 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;
}

View File

@@ -1,4 +1,4 @@
export type Van = { export type Vehicle = {
id: number; id: number;
carPlate: string; carPlate: string;
carBrand: string; carBrand: string;

View File

@@ -1,20 +1,36 @@
import { import {
IonBackButton,
IonButton, IonButton,
IonButtons,
IonCard, IonCard,
IonCardContent, IonCardContent,
IonCardHeader,
IonCardSubtitle,
IonCardTitle,
IonContent, IonContent,
IonHeader,
IonIcon, IonIcon,
IonItem,
IonItemDivider,
IonPage, IonPage,
IonRow, IonRow,
IonTitle,
IonToast,
IonToolbar,
} from "@ionic/react"; } from "@ionic/react";
import { import {
arrowForwardOutline, arrowForwardOutline,
cashOutline,
chevronForwardOutline, chevronForwardOutline,
locateOutline, locateOutline,
locationOutline, locationOutline,
personOutline,
starOutline,
timeOutline, timeOutline,
} from "ionicons/icons"; } from "ionicons/icons";
import "./BuscarTransporte.css"; import "./BuscarItinerario.css";
import itinerariesService from "../services/functions/itinerariesService";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useHistory } from "react-router"; import { useHistory } from "react-router";
@@ -23,17 +39,31 @@ import GooglePlacesAutocomplete, {
geocodeByAddress, geocodeByAddress,
getLatLng, getLatLng,
} from "react-google-places-autocomplete"; } 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 history = useHistory();
const [showToast, setShowToast] = useState(false);
const [messageToast, setMessageToast] = useState("");
const [toastColor, setToastColor] = useState<Color>("primary");
const [addressFrom, setAddressFrom] = useState<any>(""); const [addressFrom, setAddressFrom] = useState<any>("");
const [coordinatesFrom, setCoordinatesFrom] = useState<any>(""); const [coordinatesFrom, setCoordinatesFrom] = useState<any>("");
const [addressTo, setAddressTo] = useState<any>(""); const [addressTo, setAddressTo] = useState<any>("");
const [coordinatesTo, setCoordinatesTo] = useState<any>(""); const [coordinatesTo, setCoordinatesTo] = useState<any>("");
const [showModalEnd, setShowModalEnd] = useState(false); const [showModalEnd, setShowModalEnd] = useState(false);
const [addressResults, setAddressResults] = useState<any>([]); const [addressResults, setAddressResults] = useState<any[]>([]);
const [inputActive, setInputActive] = useState(""); const [inputActive, setInputActive] = useState("");
const [recentSearches, setRecentSearches] = useState<any[]>([]);
const [itinerariesList, setItinerariesList] = useState<Itinerary[]>();
// const optionsAddress = async (inputValue: any) => { // const optionsAddress = async (inputValue: any) => {
// let results = await autoCompleteAddress(inputValue) // let results = await autoCompleteAddress(inputValue)
// .then((res) => { // .then((res) => {
@@ -83,22 +113,61 @@ const BuscarTransporte: React.FC = () => {
} }
}, [addressTo]); }, [addressTo]);
function buscaTransporte() { async function buscarItinerarios() {
if (coordinatesFrom && coordinatesTo && addressFrom && addressTo) { if (!coordinatesFrom || !coordinatesTo || !addressFrom || !addressTo) {
history.push({ return;
pathname: "/transportes",
state: {
coordinatesFrom,
coordinatesTo,
addressFrom,
addressTo,
},
});
} }
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 ( return (
<IonPage> <IonPage>
<PageHeader
pageName="Buscar itinerários"
backButtonPageUrl="/buscas"
></PageHeader>
<IonContent fullscreen> <IonContent fullscreen>
<IonCard> <IonCard>
<IonCardContent> <IonCardContent>
@@ -141,32 +210,55 @@ const BuscarTransporte: React.FC = () => {
/> />
</div> </div>
<div className="button-search"> <div className="button-search">
<IonButton color="primary" onClick={() => buscaTransporte()}> <IonButton color="primary" onClick={() => buscarItinerarios()}>
Buscar Buscar
</IonButton> </IonButton>
</div> </div>
</IonCardContent> </IonCardContent>
</IonCard> </IonCard>
<IonRow class="latest-searches">
<IonIcon {recentSearches && recentSearches.length !== 0 ? (
className="icon-align-vcenter" <>
size="large" <IonItemDivider color="dark">Pesquisas recentes</IonItemDivider>
icon={timeOutline} <IonRow class="latest-searches">
></IonIcon> {recentSearches.map((search, index) => {
<div className="div_from_to"> return (
<span>Rua Tal Tal, 154, São Paulo - SP</span> <>
<IonIcon icon={arrowForwardOutline}></IonIcon> <div>
<span>USP</span> <IonRow key={index}
<br /> class="latest-searches"
<small> 1 hora</small> onClick={() => {
</div> fillSearchBars(search.addressFrom, search.addressTo);
<IonIcon }}
className="icon-forward icon-align-vcenter" >
size="large" <IonIcon
icon={chevronForwardOutline} className="icon-align-vcenter"
></IonIcon> size="large"
</IonRow> icon={timeOutline}
<IonRow class="latest-searches"> ></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 <IonIcon
className="icon-align-vcenter" className="icon-align-vcenter"
size="large" size="large"
@@ -184,7 +276,7 @@ const BuscarTransporte: React.FC = () => {
size="large" size="large"
icon={chevronForwardOutline} icon={chevronForwardOutline}
/> />
</IonRow> </IonRow> */}
{/* <IonModal isOpen={showModalEnd}> {/* <IonModal isOpen={showModalEnd}>
<IonContent> <IonContent>
<div className="header-search-modal"> <div className="header-search-modal">
@@ -225,9 +317,54 @@ const BuscarTransporte: React.FC = () => {
)} )}
</IonContent> </IonContent>
</IonModal> */} </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> </IonContent>
</IonPage> </IonPage>
); );
}; };
export default BuscarTransporte; export default BuscarItinerario;

View File

@@ -1,127 +1,163 @@
import { IonContent, IonPage, IonFab, IonFabButton, IonIcon } from '@ionic/react'; import {
import { search } from 'ionicons/icons'; IonContent,
import './BuscarPassageiro.css'; IonPage,
IonFab,
IonFabButton,
IonIcon,
} from "@ionic/react";
import { search } from "ionicons/icons";
import "./BuscarPassageiro.css";
import { Map, Marker, Overlay } from "pigeon-maps"; import { Map, Marker, Overlay } from "pigeon-maps";
import { maptiler } from 'pigeon-maps/providers'; import { maptiler } from "pigeon-maps/providers";
import { useEffect, useState } from 'react'; import { useEffect, useState } from "react";
import RecordsStore from '../../store/RecordsStore'; import RecordsStore from "../../store/RecordsStore";
import { fetchRecords } from '../../store/Selectors'; import { fetchRecords } from "../../store/Selectors";
import { getUsersSearching } from '../../services/api/users'; import { getUsersSearching } from "../../services/api/users";
import { UserSearchInfos } from '../../components/UserSearchInfos/UserSearchInfos'; 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 = () => { 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(); // getCurrentLocation();
// setCurrentPoint(fetchedLocation.currentLocation); // }, []);
// }
// 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 [results, setResults] = useState([]);
const center = { latitude: -22.907829, longitude: -47.062943 }; const [zoom, setZoom] = useState(14);
const [ results, setResults ] = useState([]); const [moveMode, setMoveMode] = useState(false);
const [ zoom, setZoom ] = useState(14);
const [ moveMode, setMoveMode ] = useState(false); // useEffect(() => {
// useEffect(() => { // const getData = async () => {
// const getData = async () => { // await getUsersSearching(currentPoint);
// }
// await getUsersSearching(currentPoint); // getData();
// } // }, [ currentPoint ]);
// getData(); useEffect(() => {
// }, [ currentPoint ]); 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); const handleMap = (e: any) => {
}, [ records ]);
const hideMarkers = () => {
console.log('entrou')
const tempRecords = JSON.parse(JSON.stringify(results));
tempRecords.forEach((tempRecord:any) => tempRecord.showInfo = false);
console.log(tempRecords)
setResults(tempRecords);
}
const handleMap = (e:any) => {
setCurrentPoint({ latitude: e.center[0], longitude: e.center[1] }); setCurrentPoint({ latitude: e.center[0], longitude: e.center[1] });
} };
const searchResults = async () => { const searchResults = async () => {
await getUsersSearching(currentPoint); 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 console.log(tempRecords);
!tempRecords[index].showInfo && tempRecords.forEach((tempRecord:any) => tempRecord.showInfo = false); setResults(tempRecords);
tempRecords[index].showInfo = !tempRecords[index].showInfo; };
console.log(tempRecords)
setResults(tempRecords);
}
return ( return (
<IonPage> <IonPage>
<IonContent fullscreen> <PageHeader
{/* { results && 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) => { {results.map((record: any, index) => {
return <Marker onClick={ e => showMarkerInfo(e, index) } key={ index } color="#3578e5" width={ 50 } anchor={ [ parseFloat(record.latitude_from), parseFloat(record.longitude_from) ] } /> 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) => { <IonFab vertical="bottom" horizontal="end" slot="fixed">
<IonFabButton>
if (record.showInfo) { <IonIcon onClick={searchResults} icon={search} />
</IonFabButton>
return ( </IonFab>
<Overlay key={ index } anchor={ [ parseFloat(record.latitude_from), parseFloat(record.longitude_from) ] } offset={[95, 304]}> {/* </> */}
<UserSearchInfos record={ record } /> {/* } */}
</Overlay> </IonContent>
) </IonPage>
}
})}
</Map>
<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
View 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;

View File

@@ -2,97 +2,54 @@ import {
IonBackButton, IonBackButton,
IonButton, IonButton,
IonButtons, IonButtons,
IonCard,
IonCardContent,
IonContent, IonContent,
IonHeader, IonHeader,
IonIcon, IonIcon,
IonItem,
IonLabel,
IonList,
IonListHeader,
IonPage, IonPage,
IonRadio,
IonRadioGroup,
IonTitle, IonTitle,
IonToolbar, IonToolbar,
} from "@ionic/react"; } from "@ionic/react";
import { close, locateOutline, locationOutline } from "ionicons/icons"; import { close, locateOutline, locationOutline } from "ionicons/icons";
import { useState } from "react";
import GooglePlacesAutocomplete from "react-google-places-autocomplete"; import GooglePlacesAutocomplete from "react-google-places-autocomplete";
import { PageHeader } from "../../components/PageHeader";
export default function CadastrarItinerario() { export default function CadastrarItinerario() {
const [selected, setSelected] = useState<any>("");
return ( return (
<IonPage> <IonPage>
<IonHeader> <PageHeader
<IonToolbar> pageName="Cadastrar itinerário"
<IonButtons slot="start"> backButtonPageUrl="/perfil"
<IonBackButton icon={close} text="" defaultHref="/perfil" /> ></PageHeader>
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent fullscreen> <IonContent fullscreen>
<IonHeader collapse="condense"> <div className="m-3">
<IonToolbar> <h1>Digite o endereço de onde você iniciará a rota do itinerário</h1>
<IonTitle size="large">Cadastrar Itinerário</IonTitle> <div className="inputs-from-to">
</IonToolbar> <IonIcon icon={locateOutline}></IonIcon>
</IonHeader> <GooglePlacesAutocomplete
<IonCard> apiKey={process.env.REACT_APP_KEY_API}
<IonCardContent> apiOptions={{ language: "pt-br", region: "br" }}
<div className="inputs-from-to"> selectProps={{
<IonIcon icon={locateOutline}></IonIcon> className: "input-autocomplete",
<GooglePlacesAutocomplete placeholder: "R. José Paulino, 1234",
apiKey={process.env.REACT_APP_KEY_API} }}
apiOptions={{ language: "pt-br", region: "br" }} />
selectProps={{ </div>
className: "input-autocomplete", <div className="inputs-from-to">
placeholder: "R. José Paulino, 1234", <IonIcon icon={locationOutline}></IonIcon>
}} <GooglePlacesAutocomplete
/> apiKey={process.env.REACT_APP_KEY_API}
</div> apiOptions={{ language: "pt-br", region: "br" }}
<div className="inputs-from-to"> selectProps={{
<IonIcon icon={locationOutline}></IonIcon> className: "input-autocomplete",
<GooglePlacesAutocomplete placeholder: "PUC Campinas",
apiKey={process.env.REACT_APP_KEY_API} }}
apiOptions={{ language: "pt-br", region: "br" }} />
selectProps={{ </div>
className: "input-autocomplete", <div className="button-search">
placeholder: "PUC Campinas", <IonButton color="primary">Cadastrar</IonButton>
}} </div>
/> </div>
</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>
</IonContent> </IonContent>
</IonPage> </IonPage>
); );

View File

@@ -9,6 +9,8 @@ import * as UsersService from '../../services/api/users'
import LocalStorage from '../../LocalStorage'; import LocalStorage from '../../LocalStorage';
import { UserContext } from '../../App'; import { UserContext } from '../../App';
import { Color } from '@ionic/core'; import { Color } from '@ionic/core';
import { closeToast } from '../../services/utils';
import { PageHeader } from '../../components/PageHeader';
const Cadastro: React.FC = () => { const Cadastro: React.FC = () => {
const history = useHistory(); const history = useHistory();
@@ -128,13 +130,11 @@ const Cadastro: React.FC = () => {
return ( return (
<IonPage> <IonPage>
<IonHeader> <PageHeader
<IonToolbar> pageName="Cadastro"
<IonButtons slot="start"> backButtonPageUrl="/login"
<IonBackButton text={''} icon={arrowBack} defaultHref='login' /> ></PageHeader>
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent fullscreen> <IonContent fullscreen>
<IonGrid className="ion-padding"> <IonGrid className="ion-padding">
<IonRow> <IonRow>
@@ -216,7 +216,7 @@ const Cadastro: React.FC = () => {
<IonToast <IonToast
color={toastColor} color={toastColor}
isOpen={showToast} isOpen={showToast}
onDidDismiss={() => setShowToast(false)} onDidDismiss={() => closeToast(setShowToast)}
message={messageToast} message={messageToast}
duration={2500} duration={2500}
/> />

View File

@@ -21,6 +21,8 @@ import { callOutline, documentTextOutline } from "ionicons/icons";
import '../Cadastro/Cadastro.css' import '../Cadastro/Cadastro.css'
import { Color } from "@ionic/core"; import { Color } from "@ionic/core";
import { closeToast } from "../../services/utils";
import { PageHeader } from "../../components/PageHeader";
interface cardItem { interface cardItem {
icon: string; icon: string;
@@ -103,14 +105,10 @@ const CadastroCompletar: React.FC = () => {
return ( return (
<IonPage> <IonPage>
<IonHeader> <PageHeader
<IonToolbar> pageName="Completar cadastro"
<IonTitle>Completar cadastro</IonTitle> backButtonPageUrl="/perfil"
<IonButtons slot="start"> ></PageHeader>
<IonBackButton defaultHref="/perfil" />
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent> <IonContent>
{ items.map((item, index) => { { items.map((item, index) => {
@@ -130,7 +128,7 @@ const CadastroCompletar: React.FC = () => {
position="top" position="top"
color={toastColor} color={toastColor}
isOpen={showToast} isOpen={showToast}
onDidDismiss={() => setShowToast(false)} onDidDismiss={() => closeToast(setShowToast)}
message={toastMessage} message={toastMessage}
duration={2500} duration={2500}
/> />

View File

@@ -28,6 +28,8 @@ import { saveOutline } from "ionicons/icons";
import * as usersRoutes from '../../services/api/users'; import * as usersRoutes from '../../services/api/users';
import validateCpf from '../../services/validateCpf' import validateCpf from '../../services/validateCpf'
import { closeToast } from "../../services/utils";
import { PageHeader } from "../../components/PageHeader";
interface documentTypesInterface { interface documentTypesInterface {
label: string; label: string;
@@ -154,22 +156,12 @@ const CompletarDocumento: React.FC = () => {
return ( return (
<IonPage> <IonPage>
<IonHeader> <PageHeader
<IonToolbar> pageName="Completar cadastro"
<IonTitle>Completar cadastro</IonTitle> backButtonPageUrl="/perfil/completar"
<IonButtons slot="start"> ></PageHeader>
<IonBackButton defaultHref="/perfil/completar" />
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent fullscreen> <IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">Completar cadastro</IonTitle>
</IonToolbar>
</IonHeader>
<IonGrid> <IonGrid>
<IonRow> <IonRow>
<IonCol> <IonCol>
@@ -206,7 +198,7 @@ const CompletarDocumento: React.FC = () => {
position="top" position="top"
color='danger' color='danger'
isOpen={showToast} isOpen={showToast}
onDidDismiss={() => setShowToast(false)} onDidDismiss={() => closeToast(setShowToast)}
message={messageToast} message={messageToast}
duration={2500} duration={2500}
/> />

View File

@@ -25,6 +25,8 @@ import { saveOutline } from "ionicons/icons";
import * as usersRoutes from '../../services/api/users'; import * as usersRoutes from '../../services/api/users';
import { Color } from "@ionic/core"; import { Color } from "@ionic/core";
import { closeToast } from "../../services/utils";
import { PageHeader } from "../../components/PageHeader";
interface documentTypesInterface { interface documentTypesInterface {
label: string; label: string;
@@ -112,22 +114,12 @@ const CompletarTelefone: React.FC = () => {
return ( return (
<IonPage> <IonPage>
<IonHeader> <PageHeader
<IonToolbar> pageName="Completar cadastro"
<IonTitle>Completar cadastro</IonTitle> backButtonPageUrl="/perfil/completar"
<IonButtons slot="start"> ></PageHeader>
<IonBackButton defaultHref="/perfil/completar" />
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent fullscreen> <IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">Completar cadastro</IonTitle>
</IonToolbar>
</IonHeader>
<IonGrid> <IonGrid>
<IonRow> <IonRow>
<IonCol> <IonCol>
@@ -156,7 +148,7 @@ const CompletarTelefone: React.FC = () => {
position="top" position="top"
color={toastColor} color={toastColor}
isOpen={showToast} isOpen={showToast}
onDidDismiss={() => setShowToast(false)} onDidDismiss={() => closeToast(setShowToast)}
message={messageToast} message={messageToast}
duration={2500} duration={2500}
/> />

View File

@@ -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;

View File

@@ -1,60 +1,58 @@
import { import {
IonContent, IonContent,
IonHeader, IonHeader,
IonPage, IonPage,
IonTitle, IonTitle,
IonToolbar IonToolbar,
} from "@ionic/react"; } from "@ionic/react";
import React, { useContext, useState } from "react"; import React, { useContext, useState } from "react";
import { IonGrid, IonRow, IonCol, IonToast } from "@ionic/react"; import { IonGrid, IonRow, IonCol, IonToast } from "@ionic/react";
import { useHistory } from "react-router-dom"; import { useHistory } from "react-router-dom";
import { import { IonItem, IonLabel, IonInput, IonButton } from "@ionic/react";
IonItem,
IonLabel,
IonInput,
IonButton,
} from "@ionic/react";
import { UserContext } from "../App"; import { UserContext } from "../App";
import { PageHeader } from "../components/PageHeader";
const Debug: React.FC = () => {
const [input, setInput] = useState(''); const Debug: React.FC = () => {
const [input, setInput] = useState("");
return (
<IonPage> return (
<IonHeader> <IonPage>
<PageHeader pageName="Debug" backButtonPageUrl="/home"></PageHeader>
<IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar> <IonToolbar>
<IonTitle>Debug</IonTitle> <IonTitle size="large">Debug</IonTitle>
</IonToolbar> </IonToolbar>
</IonHeader> </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> <IonGrid>
</IonCol> <IonRow>
</IonRow> <IonCol>
</IonGrid> <IonItem>
</IonContent> <IonLabel position="floating"> Input</IonLabel>
</IonPage> <IonInput
); type="date"
}; value={input}
// onIonChange={(e) => { setInput(e.detail.value!) }}
export default Debug; ></IonInput>
</IonItem>
<IonButton
onClick={(e) => {
setInput("1994-12-15");
console.log(input);
}}
>
Enviar
</IonButton>
</IonCol>
</IonRow>
</IonGrid>
</IonContent>
</IonPage>
);
};
export default Debug;

View File

@@ -1,11 +1,12 @@
import { IonContent, IonPage, IonToast } from '@ionic/react'; import { IonContent, IonPage, IonToast } from '@ionic/react';
import { Color } from '@ionic/core'; import { Color } from '@ionic/core';
import { useContext, useEffect, useState } from 'react'; import { useContext, useEffect, useState } from 'react';
import { useLocation } from 'react-router'; import { useHistory, useLocation } from 'react-router';
import { UserContext } from '../App'; import { UserContext } from '../App';
import * as sessionRoutes from '../services/api/session'; import * as sessionRoutes from '../services/api/session';
import { closeToast } from '../services/utils';
interface LocationState { interface LocationState {
redirectData?: { redirectData?: {
@@ -17,6 +18,7 @@ interface LocationState {
const Home: React.FC = () => { const Home: React.FC = () => {
const location = useLocation<LocationState>(); const location = useLocation<LocationState>();
const history = useHistory();
const user = useContext(UserContext); const user = useContext(UserContext);
@@ -41,6 +43,7 @@ const Home: React.FC = () => {
// setMessageToast(response.message); // setMessageToast(response.message);
// setShowToast(true); // setShowToast(true);
history.push(`/login`)
return return
} }
@@ -52,11 +55,12 @@ const Home: React.FC = () => {
// if (error.response.data.message) { // if (error.response.data.message) {
console.dir('Houve um erro: ', { error }) console.dir('Houve um erro: ', { error })
alert('Houve um erro') alert('Houve um erro')
history.push(`/login`)
}) })
} }
refreshUserToken() refreshUserToken()
}, []) }, [location.state, user, history])
return ( return (
<IonPage> <IonPage>
@@ -65,7 +69,7 @@ const Home: React.FC = () => {
position="top" position="top"
color={toastColor} color={toastColor}
isOpen={showToast} isOpen={showToast}
onDidDismiss={() => setShowToast(false)} onDidDismiss={() => closeToast(setShowToast)}
message={toastMessage} message={toastMessage}
duration={2500} duration={2500}
/> />

147
src/pages/Itinerario.tsx Normal file
View 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;

View File

@@ -3,30 +3,27 @@ import {
IonHeader, IonHeader,
IonPage, IonPage,
IonTitle, IonTitle,
IonToolbar IonToolbar,
} from "@ionic/react"; } from "@ionic/react";
import React, { useContext, useState } from "react"; import React, { useContext, useState } from "react";
import { IonGrid, IonRow, IonCol, IonToast } from "@ionic/react"; import { IonGrid, IonRow, IonCol, IonToast } from "@ionic/react";
import { useHistory } from "react-router-dom"; import { useHistory } from "react-router-dom";
import { import { IonItem, IonLabel, IonInput, IonButton } from "@ionic/react";
IonItem,
IonLabel,
IonInput,
IonButton,
} from "@ionic/react";
import * as sessionRoutes from '../services/api/session'; import * as sessionRoutes from "../services/api/session";
import LocalStorage from '../LocalStorage'; import LocalStorage from "../LocalStorage";
import { Action } from "../components/Action"; import { Action } from "../components/Action";
import { UserContext } from "../App"; import { UserContext } from "../App";
import { closeToast } from "../services/utils";
import { PageHeader } from "../components/PageHeader";
const Page: React.FC = () => { const Page: React.FC = () => {
const [showToast, setShowToast] = useState(false); const [showToast, setShowToast] = useState(false);
const [messageToast, setMessageToast ] = useState(''); const [messageToast, setMessageToast] = useState("");
const history = useHistory(); const history = useHistory();
const [email, setEmail] = useState(''); const [email, setEmail] = useState("");
const [password, setPassword] = useState(''); const [password, setPassword] = useState("");
const user = useContext(UserContext); const user = useContext(UserContext);
@@ -56,18 +53,18 @@ const Page: React.FC = () => {
return false; 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"); setMessageToast("A senha deve conter entre 7 e 12 dígitos");
setShowToast(true); setShowToast(true);
return false; return false;
} }
return true; return true;
} };
const handleLogin = async () => { const handleLogin = async () => {
if (!validateForm()) { if (!validateForm()) {
return return;
} }
const singinForm = { const singinForm = {
@@ -75,50 +72,48 @@ const Page: React.FC = () => {
password: password, password: password,
}; };
await sessionRoutes.create(singinForm).then(response => { await sessionRoutes
if (response.status === 'error') { .create(singinForm)
setMessageToast(response.message); .then((response) => {
setShowToast(true); 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: { history.push({
showToastMessage: true, pathname: "/home",
toastColor: "success", state: {
toastMessage: "Usuário autenticado com sucesso!", redirectData: {
}}}) showToastMessage: true,
}).catch(error => { toastColor: "success",
// if (!error.response) return toastMessage: "Usuário autenticado com sucesso!",
},
},
});
})
.catch((error) => {
// if (!error.response) return
// se o backend retornou uma mensagem de erro customizada // se o backend retornou uma mensagem de erro customizada
// if (error.response.data.message) { // if (error.response.data.message) {
console.dir('Houve um erro: ', { error }) console.dir("Houve um erro: ", { error });
alert('Houve um erro') alert("Houve um erro");
}) });
}; };
return ( return (
<IonPage> <IonPage>
<IonHeader> <PageHeader pageName="Login"></PageHeader>
<IonToolbar>
<IonTitle>Login</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent fullscreen> <IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">Login</IonTitle>
</IonToolbar>
</IonHeader>
<IonGrid> <IonGrid>
<IonRow> <IonRow>
<IonCol> <IonCol>
@@ -152,7 +147,11 @@ const Page: React.FC = () => {
Login Login
</IonButton> </IonButton>
<p style={{ fontSize: "medium" }}> <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> </p>
</IonCol> </IonCol>
</IonRow> </IonRow>
@@ -160,9 +159,9 @@ const Page: React.FC = () => {
<IonToast <IonToast
position="top" position="top"
color='danger' color="danger"
isOpen={showToast} isOpen={showToast}
onDidDismiss={() => setShowToast(false)} onDidDismiss={() => closeToast(setShowToast)}
message={messageToast} message={messageToast}
duration={2500} duration={2500}
/> />

View File

@@ -16,11 +16,12 @@ import {
} from "@ionic/react"; } from "@ionic/react";
import { add, locateOutline, locationOutline } from "ionicons/icons"; import { add, locateOutline, locationOutline } from "ionicons/icons";
import { useState } from "react"; import { useState } from "react";
import { PageHeader } from "../../components/PageHeader";
import "./MeusItinerarios.css"; import "./MeusItinerarios.css";
interface ItineraryInfo { interface ItineraryInfo {
id_itinerary: number; id_itinerary: number;
van_plate: string; vehicle_plate: string;
days_of_week: number; days_of_week: number;
specific_day: string; specific_day: string;
estimated_departure_time: string; estimated_departure_time: string;
@@ -35,7 +36,7 @@ export default function MeusItinerarios() {
[ [
{ {
id_itinerary: 1, id_itinerary: 1,
van_plate: 'FSS1918', vehicle_plate: 'FSS1918',
days_of_week: 3, days_of_week: 3,
specific_day: '24/08/2022', specific_day: '24/08/2022',
estimated_departure_time: '10:00', estimated_departure_time: '10:00',
@@ -46,7 +47,7 @@ export default function MeusItinerarios() {
}, },
{ {
id_itinerary: 1, id_itinerary: 1,
van_plate: 'FSS1918', vehicle_plate: 'FSS1918',
days_of_week: 3, days_of_week: 3,
specific_day: '24/08/2022', specific_day: '24/08/2022',
estimated_departure_time: '10:00', estimated_departure_time: '10:00',
@@ -57,7 +58,7 @@ export default function MeusItinerarios() {
}, },
{ {
id_itinerary: 1, id_itinerary: 1,
van_plate: 'FSS1918', vehicle_plate: 'FSS1918',
days_of_week: 3, days_of_week: 3,
specific_day: '24/08/2022', specific_day: '24/08/2022',
estimated_departure_time: '10:00', estimated_departure_time: '10:00',
@@ -68,7 +69,7 @@ export default function MeusItinerarios() {
}, },
{ {
id_itinerary: 1, id_itinerary: 1,
van_plate: 'FSS1918', vehicle_plate: 'FSS1918',
days_of_week: 3, days_of_week: 3,
specific_day: '24/08/2022', specific_day: '24/08/2022',
estimated_departure_time: '10:00', estimated_departure_time: '10:00',
@@ -79,7 +80,7 @@ export default function MeusItinerarios() {
}, },
{ {
id_itinerary: 1, id_itinerary: 1,
van_plate: 'FSS1918', vehicle_plate: 'FSS1918',
days_of_week: 3, days_of_week: 3,
specific_day: '24/08/2022', specific_day: '24/08/2022',
estimated_departure_time: '10:00', estimated_departure_time: '10:00',
@@ -93,20 +94,12 @@ export default function MeusItinerarios() {
return ( return (
<IonPage> <IonPage>
<IonHeader translucent> <PageHeader
<IonToolbar> pageName="Meus Itinerários"
<IonTitle>Meus Itinerários</IonTitle> backButtonPageUrl="/perfil"
<IonButtons slot="start"> ></PageHeader>
<IonBackButton text={""} defaultHref="/perfil" />
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent fullscreen> <IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">Meus Itinerários</IonTitle>
</IonToolbar>
</IonHeader>
{routes ? ( {routes ? (
routes.map((itinerary, index) => { routes.map((itinerary, index) => {
return ( return (

153
src/pages/MeusVeiculos.tsx Normal file
View 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;

View File

@@ -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;

View File

@@ -40,6 +40,8 @@ import sessionsService from "../services/functions/sessionsService";
import usersService from "../services/functions/usersService"; import usersService from "../services/functions/usersService";
import { UserContext } from "../App"; import { UserContext } from "../App";
import { Color } from "@ionic/core"; import { Color } from "@ionic/core";
import { closeToast } from "../services/utils";
import { PageHeader } from "../components/PageHeader";
interface ScanNewProps { interface ScanNewProps {
match: { match: {
@@ -146,7 +148,7 @@ const Perfil: React.FC<ScanNewProps> = (props) => {
return; 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); const userIsDriverRes = await usersService.checkIfUserIsDriver(userId);
// if (userIsDriverRes.error) { // if (userIsDriverRes.error) {
@@ -211,22 +213,9 @@ const Perfil: React.FC<ScanNewProps> = (props) => {
return ( return (
<IonPage> <IonPage>
<IonHeader translucent> <PageHeader pageName="Meu perfil"></PageHeader>
<IonToolbar>
<IonTitle>Seu perfil</IonTitle>
<IonButtons slot="start">
<IonBackButton text="" defaultHref="/home" />
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent fullscreen> <IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">Seu perfil</IonTitle>
</IonToolbar>
</IonHeader>
<IonCard> <IonCard>
<IonCardContent> <IonCardContent>
<img <img
@@ -334,20 +323,20 @@ const Perfil: React.FC<ScanNewProps> = (props) => {
<IonItem <IonItem
button button
onClick={() => history.push({ pathname: "/cadastro-van" })} onClick={() => history.push({ pathname: "/veiculos/cadastrar" })}
> >
<IonIcon icon={carOutline} slot="start" /> <IonIcon icon={carOutline} slot="start" />
<IonLabel>Cadastrar van</IonLabel> <IonLabel>Cadastrar veículo</IonLabel>
</IonItem> </IonItem>
{isDriver ? ( {isDriver ? (
<> <>
<IonItem <IonItem
button button
onClick={() => history.push({ pathname: "/minhas-vans" })} onClick={() => history.push({ pathname: "/veiculos/meus" })}
> >
<IonIcon icon={carOutline} slot="start" /> <IonIcon icon={carOutline} slot="start" />
<IonLabel>Minhas vans</IonLabel> <IonLabel>Meus veículos</IonLabel>
</IonItem> </IonItem>
<IonItem <IonItem
button button
@@ -393,7 +382,7 @@ const Perfil: React.FC<ScanNewProps> = (props) => {
position="top" position="top"
color={toastColor} color={toastColor}
isOpen={showToast} isOpen={showToast}
onDidDismiss={() => setShowToast(false)} onDidDismiss={() => closeToast(setShowToast)}
message={toastMessage} message={toastMessage}
duration={2500} duration={2500}
/> />

View File

@@ -14,21 +14,23 @@ import {
IonTextarea, IonTextarea,
IonTitle, IonTitle,
IonToast, IonToast,
IonToolbar IonToolbar,
} from "@ionic/react"; } from "@ionic/react";
import React, { useEffect, useReducer, useState } from "react"; import React, { useEffect, useReducer, useState } from "react";
import { IonRow, IonCol } from "@ionic/react"; import { IonRow, IonCol } from "@ionic/react";
import './Perfil.css' import "./Perfil.css";
import { useHistory, useLocation } from "react-router"; import { useHistory, useLocation } from "react-router";
import { saveOutline } from "ionicons/icons"; 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 { Color } from "@ionic/core";
import { closeToast } from "../services/utils";
import { PageHeader } from "../components/PageHeader";
interface userData { interface userData {
name: string; name: string;
@@ -39,7 +41,7 @@ interface userData {
} }
interface LocationState { interface LocationState {
userData: userData userData: userData;
} }
const PerfilEditar: React.FC = () => { const PerfilEditar: React.FC = () => {
@@ -47,97 +49,99 @@ const PerfilEditar: React.FC = () => {
const location = useLocation<LocationState>(); const location = useLocation<LocationState>();
const [showToast, setShowToast] = useState(false); const [showToast, setShowToast] = useState(false);
const [messageToast, setMessageToast] = useState(''); const [messageToast, setMessageToast] = useState("");
const [toastColor, setToastColor] = useState<Color>("primary"); const [toastColor, setToastColor] = useState<Color>("primary");
const [userData, setUserData] = useState({ const [userData, setUserData] = useState({
name: '', name: "",
lastname: '', lastname: "",
email: '', email: "",
birth_date: '', birth_date: "",
bio: '', bio: "",
}); });
const [inputValues, setInputValues] = useReducer( const [inputValues, setInputValues] = useReducer(
(state: any, newState: any) => ({ ...state, ...newState }), (state: any, newState: any) => ({ ...state, ...newState }),
{ {
name: '', name: "",
lastname: '', lastname: "",
email: '', email: "",
birth_date: '', birth_date: "",
bio: '', bio: "",
} }
); );
useEffect(() => { useEffect(() => {
if (!location.state) { 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({ setInputValues({
'name': userData.name, name: userData.name,
'lastname': userData.lastname, lastname: userData.lastname,
'email': userData.email, email: userData.email,
'birth_date': userData.birth_date, birth_date: userData.birth_date,
'bio': userData.bio bio: userData.bio,
}); });
console.log(inputValues)
}, [userData]); }, [userData]);
const handleUpdateUserData = () => { const handleUpdateUserData = () => {
usersRoutes.update(inputValues).then(response => { usersRoutes
if (response.status === 'error') { .update(inputValues)
setToastColor("danger") .then((response) => {
setMessageToast(response.message); 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); 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 = () => { const hasChangedSinceInitialState = () => {
return isEqual(userData, inputValues) return isEqual(userData, inputValues);
} };
return ( return (
<IonPage> <IonPage>
<IonHeader> <PageHeader
<IonToolbar> pageName="Editar perfil"
<IonTitle>Editar perfil</IonTitle> backButtonPageUrl="/perfil"
<IonButtons slot="start"> ></PageHeader>
<IonBackButton defaultHref="/perfil" />
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent> <IonContent>
<IonGrid> <IonGrid>
<IonRow> <IonRow>
<IonCol size="12"> <IonCol size="12">
<div id='nome-sobrenome'> <div id="nome-sobrenome">
<IonItem> <IonItem>
<IonLabel position="stacked"> Nome</IonLabel> <IonLabel position="stacked"> Nome</IonLabel>
<IonInput <IonInput
type="text" type="text"
value={inputValues.name} value={inputValues.name}
onIonChange={(e) => setInputValues({'name': e.detail.value!})} onIonChange={(e) =>
setInputValues({ name: e.detail.value! })
}
></IonInput> ></IonInput>
</IonItem> </IonItem>
@@ -146,7 +150,9 @@ const PerfilEditar: React.FC = () => {
<IonInput <IonInput
type="text" type="text"
value={inputValues.lastname} value={inputValues.lastname}
onIonChange={(e) => setInputValues({'lastname': e.detail.value!})} onIonChange={(e) =>
setInputValues({ lastname: e.detail.value! })
}
></IonInput> ></IonInput>
</IonItem> </IonItem>
</div> </div>
@@ -156,25 +162,28 @@ const PerfilEditar: React.FC = () => {
<IonInput <IonInput
type="email" type="email"
value={inputValues.email} value={inputValues.email}
onIonChange={(e) => setInputValues({'email': e.detail.value!})} onIonChange={(e) =>
setInputValues({ email: e.detail.value! })
}
></IonInput> ></IonInput>
</IonItem> </IonItem>
<IonItem> <IonItem>
<IonLabel position='stacked'>Data de nascimento</IonLabel> <IonLabel position="stacked">Data de nascimento</IonLabel>
<IonInput <IonInput
type='date' type="date"
value={inputValues.birth_date} value={inputValues.birth_date}
onIonChange={(e) => setInputValues({'birth_date': e.detail.value!}) } onIonChange={(e) =>
> setInputValues({ birth_date: e.detail.value! })
</IonInput> }
></IonInput>
</IonItem> </IonItem>
<IonItem> <IonItem>
<IonLabel position="stacked"> Biografia</IonLabel> <IonLabel position="stacked"> Biografia</IonLabel>
<IonTextarea <IonTextarea
value={inputValues.bio} value={inputValues.bio}
onIonChange={(e) => setInputValues({'bio': e.detail.value!})} onIonChange={(e) => setInputValues({ bio: e.detail.value! })}
></IonTextarea> ></IonTextarea>
</IonItem> </IonItem>
</IonCol> </IonCol>
@@ -182,7 +191,10 @@ const PerfilEditar: React.FC = () => {
</IonGrid> </IonGrid>
<IonFab vertical="bottom" horizontal="end" slot="fixed"> <IonFab vertical="bottom" horizontal="end" slot="fixed">
<IonFabButton disabled={hasChangedSinceInitialState()} onClick={handleUpdateUserData}> <IonFabButton
disabled={hasChangedSinceInitialState()}
onClick={handleUpdateUserData}
>
<IonIcon icon={saveOutline} /> <IonIcon icon={saveOutline} />
</IonFabButton> </IonFabButton>
</IonFab> </IonFab>
@@ -190,7 +202,7 @@ const PerfilEditar: React.FC = () => {
<IonToast <IonToast
color={toastColor} color={toastColor}
isOpen={showToast} isOpen={showToast}
onDidDismiss={() => setShowToast(false)} onDidDismiss={() => closeToast(setShowToast)}
message={messageToast} message={messageToast}
duration={2500} duration={2500}
/> />

View File

@@ -45,9 +45,10 @@ import {
} from "ionicons/icons"; } from "ionicons/icons";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useHistory, useLocation } from "react-router"; 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 { createUserSearch } from "../../services/api/users";
import "./Transportes.css"; import "./Transportes.css";
import { closeToast } from "../../services/utils";
interface InfoBusca { interface InfoBusca {
addressFrom: any; addressFrom: any;
@@ -60,7 +61,7 @@ const Transportes: React.FC = () => {
const history = useHistory(); const history = useHistory();
const location = useLocation(); const location = useLocation();
const props = location.state as InfoBusca; const props = location.state as InfoBusca;
const [transportes, setTransportes] = useState([]); const [itinerarios, setItinerarios] = useState([]);
const [showModalFilters, setShowModalFilters] = useState(false); const [showModalFilters, setShowModalFilters] = useState(false);
const [showToast, setShowToast] = useState(false); const [showToast, setShowToast] = useState(false);
const [messageToast, setMessageToast ] = useState(''); const [messageToast, setMessageToast ] = useState('');
@@ -68,13 +69,13 @@ const Transportes: React.FC = () => {
useEffect(() => { useEffect(() => {
if (props) { if (props) {
buscaTransportes(); buscaItinerarios();
} }
}, [props]); }, [props]);
async function buscaTransportes() { async function buscaItinerarios() {
let data = (await getTransportes(props)) as any; let data = (await itinerariesService.searchItineraries(props)) as any;
setTransportes(data); setItinerarios(data);
} }
function criaAlerta(){ function criaAlerta(){
@@ -90,6 +91,7 @@ const Transportes: React.FC = () => {
return ( return (
<IonPage> <IonPage>
{/* TODO, componentizar Header */}
<IonHeader> <IonHeader>
<div className="header-page"> <div className="header-page">
{/* <IonButtons slot="start"> {/* <IonButtons slot="start">
@@ -107,7 +109,7 @@ const Transportes: React.FC = () => {
</div> </div>
</IonHeader> </IonHeader>
<IonContent fullscreen> <IonContent fullscreen>
{transportes && transportes.length > 0? ( {itinerarios && itinerarios.length > 0? (
<div className="header-tabs"> <div className="header-tabs">
<IonSlides> <IonSlides>
<IonSlide> <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>)} (<h1 className="msg-not-found">Não foi encontrado nenhum transporte que atenda essa rota.</h1>)}
{transportes && {itinerarios &&
transportes.map((record: any, index: any) => { itinerarios.map((record: any, index: any) => {
return ( return (
<IonCard className="card-transporte" key={index}> <IonCard className="card-transporte" key={index}>
<IonCardContent> <IonCardContent>
@@ -210,7 +212,7 @@ const Transportes: React.FC = () => {
// cssClass={"toast-notification"} // cssClass={"toast-notification"}
color={toastColor} color={toastColor}
isOpen={showToast} isOpen={showToast}
onDidDismiss={() => setShowToast(false)} onDidDismiss={() => closeToast(setShowToast)}
message={messageToast} message={messageToast}
duration={2500} duration={2500}
/> />

View 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"> 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;

View 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;
}

View File

@@ -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;
}

View File

@@ -1,6 +1,6 @@
import instance from "./api"; import instance from "./api";
import vansRoutes from "../../constants/routes/vansRoutes"; import vehiclesRoutes from "../../constants/routes/vehiclesRoutes";
import { AxiosRequestHeaders } from "axios"; import { AxiosRequestHeaders } from "axios";
import LocalStorage from "../../LocalStorage"; import LocalStorage from "../../LocalStorage";
@@ -17,10 +17,10 @@ function updateHeader() {
}; };
} }
export async function getByPlate(vanId: string) { export async function getByPlate(vehicleId: string) {
updateHeader(); updateHeader();
const response = await instance.get(vansRoutes.getByPlate.url + `/${vanId}`, { const response = await instance.get(vehiclesRoutes.getByPlate.url + `/${vehicleId}`, {
headers: header, headers: header,
}); });
@@ -30,14 +30,14 @@ export async function getByPlate(vanId: string) {
export async function getByUserId(userId: string) { export async function getByUserId(userId: string) {
updateHeader(); updateHeader();
const response = await instance.get(vansRoutes.getByUserId.url + `/${userId}`, { const response = await instance.get(vehiclesRoutes.getByUserId.url + `/${userId}`, {
headers: header, headers: header,
}); });
return response.data; return response.data;
} }
interface CreateVanBody { interface CreateVehicleBody {
plate: string; plate: string;
brand: string; brand: string;
model: string; model: string;
@@ -49,23 +49,23 @@ interface CreateVanBody {
locator_state: string; locator_state: string;
} }
export async function create(CreateVanBody: CreateVanBody) { export async function create(CreateVehicleBody: CreateVehicleBody) {
updateHeader(); 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; return response.data;
} }
interface UpdateVanBody { interface UpdateVehicleBody {
brand?: string; brand?: string;
model?: string; model?: string;
seats_number?: string; seats_number?: string;
} }
export async function update(vanData: UpdateVanBody) { export async function update(vehicleData: UpdateVehicleBody) {
updateHeader(); updateHeader();
const response = await instance.patch(vansRoutes.update.url, vanData, { const response = await instance.patch(vehiclesRoutes.update.url, vehicleData, {
headers: header, headers: header,
}); });

View 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 }

View File

@@ -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) {
}
}

View File

@@ -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`); const response = await instance.get(`https://api.geoapify.com/v1/geocode/autocomplete?text=${address}&apiKey=ee574aacff6f440a84378bbbf7e2f20d`);
return response.data.features; 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
View File

@@ -0,0 +1,4 @@
/* ./src/theme/tailwind.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

12
tailwind.config.js Normal file
View 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: [],
}

18100
yarn.lock

File diff suppressed because it is too large Load Diff