Merge pull request #17 from Hzin/feature/VAN-16-view-create-itinerary

Feature/VAN-16-view-create-itinerary
This commit is contained in:
Hugo Falcão
2022-09-18 19:25:45 -03:00
committed by GitHub
30 changed files with 11421 additions and 59330 deletions

View File

@@ -1 +1,7 @@
# tcc-vamos-frontend # tcc-vamos-frontend
# Primeiros passos para iniciar o desenvolvimento
- Rode o comando ```yarn``` para instalar as dependencias do projeto
- Para iniciar o desenvolvimento de uma nova funcionalidade crie uma branch
- Ao terminar o desenvolvimento abra um pull request para a branch "develop" e resolva os conflitos caso tenha

48515
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -9,13 +9,15 @@
"@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", "@craco/craco": "^6.4.5",
"@googlemaps/js-api-loader": "^1.14.3",
"@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.2.5",
"@ionic/react-router": "^6.0.0", "@ionic/react-router": "^6.2.5",
"@testing-library/jest-dom": "^5.11.9", "@testing-library/jest-dom": "^5.11.9",
"@testing-library/react": "^11.2.5", "@testing-library/react": "^11.2.5",
"@testing-library/user-event": "^12.6.3", "@testing-library/user-event": "^12.6.3",
"@types/google.maps": "^3.50.0",
"@types/jest": "^26.0.20", "@types/jest": "^26.0.20",
"@types/node": "^12.19.15", "@types/node": "^12.19.15",
"@types/react": "^18.0.18", "@types/react": "^18.0.18",
@@ -23,6 +25,7 @@
"@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",
"caniuse-lite": "1.0.30001393",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"ionicons": "^5.4.0", "ionicons": "^5.4.0",
"lodash.isequal": "^4.5.0", "lodash.isequal": "^4.5.0",
@@ -30,7 +33,6 @@
"pullstate": "^1.24.0", "pullstate": "^1.24.0",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.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",
@@ -84,7 +86,11 @@
"@types/lodash.isequal": "^4.5.6", "@types/lodash.isequal": "^4.5.6",
"autoprefixer": "^9.8.8", "autoprefixer": "^9.8.8",
"postcss": "^7.0.39", "postcss": "^7.0.39",
"react-error-overlay": "6.0.9",
"tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.2.17" "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",
"resolutions": {
"react-error-overlay": "6.0.9"
}
} }

View File

@@ -1,4 +1,3 @@
import React, { useContext, useState } from "react";
import { import {
IonApp, IonApp,
IonIcon, IonIcon,
@@ -10,6 +9,7 @@ import {
setupIonicReact, setupIonicReact,
} from "@ionic/react"; } from "@ionic/react";
import { IonReactRouter } from "@ionic/react-router"; import { IonReactRouter } from "@ionic/react-router";
import React, { useContext, useState } from "react";
import { Redirect, Route } from "react-router-dom"; import { Redirect, Route } from "react-router-dom";
import { home, person, search } from "ionicons/icons"; import { home, person, search } from "ionicons/icons";
@@ -25,15 +25,14 @@ import Login from "./pages/Login";
import Perfil from "./pages/Perfil"; import Perfil from "./pages/Perfil";
import PerfilEditar from "./pages/PerfilEditar"; import PerfilEditar from "./pages/PerfilEditar";
import VeiculoCadastro from "./pages/VeiculoCadastro";
import MeusVeiculos from "./pages/MeusVeiculos";
import CadastrarItinerario from "./pages/CadastrarItinerario/CadastrarItinerario"; import CadastrarItinerario from "./pages/CadastrarItinerario/CadastrarItinerario";
import MeusItinerarios from "./pages/MeusItinerarios/MeusItinerarios"; import MeusItinerarios from "./pages/MeusItinerarios/MeusItinerarios";
import MeusVeiculos from "./pages/MeusVeiculos";
import VeiculoCadastro from "./pages/VeiculoCadastro";
import Buscas from "./pages/Buscas";
import BuscarItinerario from "./pages/BuscarItinerario"; import BuscarItinerario from "./pages/BuscarItinerario";
import BuscarPassageiro from "./pages/BuscarPassageiro/BuscarPassageiro"; import BuscarPassageiro from "./pages/BuscarPassageiro/BuscarPassageiro";
import Transportes from "./pages/Transportes/Transportes"; import ListaItinerarios from "./pages/ListaItinerarios";
/* 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";
@@ -56,6 +55,7 @@ import "./theme/variables.css";
/* Tailwind styles */ /* Tailwind styles */
import "./theme/tailwind.css"; import "./theme/tailwind.css";
import Buscas from "./pages/Buscas";
setupIonicReact(); setupIonicReact();
@@ -93,7 +93,7 @@ const routes = (
<Route exact path="/buscas" component={Buscas}></Route> <Route exact path="/buscas" component={Buscas}></Route>
<Route exact path="/buscar/itinerario" component={BuscarItinerario}></Route> <Route exact path="/buscar/itinerario" component={BuscarItinerario}></Route>
<Route exact path="/buscar/passageiro" component={BuscarPassageiro}></Route> <Route exact path="/buscar/passageiro" component={BuscarPassageiro}></Route>
<Route exact path="/transportes" component={Transportes}></Route> <Route exact path="/buscar/itinerario/lista" component={ListaItinerarios}></Route>
<Route exact path="/"> <Route exact path="/">
<Redirect to="/home" /> <Redirect to="/home" />

View File

@@ -1,24 +1,21 @@
const tokenId = 'token';
const productDetails = '@productDetails';
const LocalStorage = { const LocalStorage = {
getToken: (): string => { getToken: (): string => {
const tokenId = localStorage.getItem('tokenId') const tokenId = localStorage.getItem("tokenId");
if (!tokenId) { if (!tokenId) {
return '' return "";
} }
return tokenId return tokenId;
}, },
setToken: (token: string) => { setToken: (token: string) => {
localStorage.setItem('tokenId', token) localStorage.setItem("tokenId", token);
}, },
clearToken: () => { clearToken: () => {
localStorage.removeItem('tokenId') localStorage.removeItem("tokenId");
} },
} };
export default LocalStorage export default LocalStorage;

View File

@@ -0,0 +1,103 @@
import { Loader } from "@googlemaps/js-api-loader";
import { InputHTMLAttributes, useEffect, useRef } from "react";
const apiKey = process.env.REACT_APP_KEY_API
? process.env.REACT_APP_KEY_API
: "";
const extractAddress = (place: any) => {
const address = {
formatted_address: "",
lat: 0,
lng: 0,
};
if (place.formatted_address) {
address.formatted_address = place.formatted_address;
}
if (place.geometry && place.geometry.location) {
address.lat = place.geometry.location.lat();
address.lng = place.geometry.location.lng();
}
return address;
};
interface AddressSelected {
formatted_address: string;
lat: number;
lng: number;
}
interface AutoCompleteInputProps extends InputHTMLAttributes<HTMLInputElement> {
onAddressSelected: (address: AddressSelected) => void;
clearAfterSelect?: boolean;
}
function AutoCompleteInput(props: AutoCompleteInputProps) {
const searchInput = useRef(null);
const { onAddressSelected, clearAfterSelect, ...othersProps } = props;
// do something on address change
const onChangeAddress = (autocomplete: any) => {
const place = autocomplete.getPlace();
const extractedAddress = extractAddress(place);
props.onAddressSelected && props.onAddressSelected(extractedAddress);
};
// init autocomplete
const initAutocomplete = () => {
if (!searchInput.current) return;
const autocomplete = new window.google.maps.places.Autocomplete(
searchInput.current
);
autocomplete.setFields(["formatted_address", "geometry"]);
autocomplete.setComponentRestrictions({ country: "br" });
autocomplete.addListener("place_changed", () =>
onChangeAddress(autocomplete)
);
};
// load map script after mounted
useEffect(() => {
const init = async () => {
try {
if (
!window.google ||
!window.google.maps ||
!window.google.maps.places
) {
await new Loader({
apiKey,
version: "weekly",
libraries: ["places"],
language: "pt-BR",
}).load();
}
initAutocomplete();
} catch (error) {
console.log(error);
}
};
if (apiKey) init();
}, []);
return (
<input
ref={searchInput}
type="text"
{...othersProps}
style={{
textIndent: "0.5rem",
width: "100%",
height: "2.5rem",
borderRadius: "0.25rem",
}}
/>
);
}
export default AutoCompleteInput;

View File

@@ -1,4 +1,10 @@
import { IonBackButton, IonButtons, IonHeader, IonTitle, IonToolbar } from "@ionic/react"; import {
IonBackButton,
IonButtons,
IonHeader,
IonTitle,
IonToolbar,
} from "@ionic/react";
interface ComponentProps { interface ComponentProps {
pageName: string; pageName: string;
@@ -10,7 +16,12 @@ export const PageHeader = (props: ComponentProps) => (
<IonToolbar> <IonToolbar>
<IonTitle>{props.pageName}</IonTitle> <IonTitle>{props.pageName}</IonTitle>
<IonButtons slot="start"> <IonButtons slot="start">
<IonBackButton text="" defaultHref={ props.backButtonPageUrl ? props.backButtonPageUrl : undefined } /> <IonBackButton
text=""
defaultHref={
props.backButtonPageUrl ? props.backButtonPageUrl : undefined
}
/>
</IonButtons> </IonButtons>
</IonToolbar> </IonToolbar>
</IonHeader> </IonHeader>

View File

@@ -4,15 +4,9 @@ import {
IonCardSubtitle, IonCardSubtitle,
IonCol, IonCol,
IonIcon, IonIcon,
IonNote,
IonRow, IonRow,
} from "@ionic/react"; } from "@ionic/react";
import { import { call, callOutline, navigateOutline } from "ionicons/icons";
arrowForward,
call,
callOutline,
navigateOutline,
} from "ionicons/icons";
import "./UserSearchInfos.css"; import "./UserSearchInfos.css";
export const UserSearchInfos = (record: any) => { export const UserSearchInfos = (record: any) => {
@@ -28,7 +22,7 @@ export const UserSearchInfos = (record: any) => {
</IonBadge> </IonBadge>
<p> <p>
<IonIcon icon={navigateOutline} size='large' /> <IonIcon icon={navigateOutline} size="large" />
&nbsp;{record.record.address_to} &nbsp;{record.record.address_to}
</p> </p>

View File

@@ -6,6 +6,8 @@
.button-search{ .button-search{
display: flex; display: flex;
justify-content: center; justify-content: center;
margin-top: 50%;
} }
.latest-searches{ .latest-searches{

View File

@@ -1,49 +1,39 @@
import { import {
IonBackButton,
IonButton, IonButton,
IonButtons,
IonCard, IonCard,
IonCardContent, IonCardContent,
IonCardHeader,
IonCardSubtitle,
IonCardTitle,
IonContent, IonContent,
IonHeader,
IonIcon, IonIcon,
IonItem,
IonItemDivider, IonItemDivider,
IonPage, IonPage,
IonRow, IonRow,
IonTitle,
IonToast, 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 "./BuscarItinerario.css"; import "./BuscarItinerario.css";
import itinerariesService from "../services/functions/itinerariesService"; import { useState } from "react";
import { useEffect, useState } from "react";
import { useHistory } from "react-router"; import { useHistory } from "react-router";
import GooglePlacesAutocomplete, {
geocodeByAddress,
getLatLng,
} from "react-google-places-autocomplete";
import { Itinerary } from "../models/itinerary.model";
import { PageHeader } from "../components/PageHeader"; import { PageHeader } from "../components/PageHeader";
import { Itinerary } from "../models/itinerary.model";
import { closeToast } from "../services/utils"; import { closeToast } from "../services/utils";
import { Color } from "@ionic/core"; import { Color } from "@ionic/core";
import AutoCompleteInput from "../components/AutoCompleteInput";
import { searchItineraries } from "../services/functions/itinerariesService";
interface Address {
formatted_address: string;
lat: number;
lng: number;
}
const BuscarItinerario: React.FC = () => { const BuscarItinerario: React.FC = () => {
const history = useHistory(); const history = useHistory();
@@ -52,13 +42,10 @@ const BuscarItinerario: React.FC = () => {
const [messageToast, setMessageToast] = useState(""); const [messageToast, setMessageToast] = useState("");
const [toastColor, setToastColor] = useState<Color>("primary"); const [toastColor, setToastColor] = useState<Color>("primary");
const [addressFrom, setAddressFrom] = useState<any>(""); const [addressFrom, setAddressFrom] = useState<Address>();
const [coordinatesFrom, setCoordinatesFrom] = useState<any>(""); const [coordinatesFrom, setCoordinatesFrom] = useState<any>("");
const [addressTo, setAddressTo] = useState<any>(""); const [addressTo, setAddressTo] = useState<Address>();
const [coordinatesTo, setCoordinatesTo] = useState<any>(""); const [coordinatesTo, setCoordinatesTo] = useState<any>("");
const [showModalEnd, setShowModalEnd] = useState(false);
const [addressResults, setAddressResults] = useState<any[]>([]);
const [inputActive, setInputActive] = useState("");
const [recentSearches, setRecentSearches] = useState<any[]>([]); const [recentSearches, setRecentSearches] = useState<any[]>([]);
@@ -97,46 +84,33 @@ const BuscarItinerario: React.FC = () => {
// setShowModalEnd(false); // setShowModalEnd(false);
// } // }
useEffect(() => {
if (addressFrom.label && addressFrom.label.length > 0) {
geocodeByAddress(addressFrom.label)
.then((results) => getLatLng(results[0]))
.then(({ lat, lng }) => setCoordinatesFrom({ lat, lng }));
}
}, [addressFrom]);
useEffect(() => {
if (addressTo.label && addressTo.label.length > 0) {
geocodeByAddress(addressTo.label)
.then((results) => getLatLng(results[0]))
.then(({ lat, lng }) => setCoordinatesTo({ lat, lng }));
}
}, [addressTo]);
async function buscarItinerarios() { async function buscarItinerarios() {
if (!coordinatesFrom || !coordinatesTo || !addressFrom || !addressTo) { if (!addressFrom || !addressTo) {
return; return;
} }
const maxRecentSearchesLength = 0 const maxRecentSearchesLength = 0;;
if (recentSearches.length >= maxRecentSearchesLength) { if (recentSearches.length >= maxRecentSearchesLength) {
setRecentSearches(recentSearches.slice(recentSearches.length - maxRecentSearchesLength)); setRecentSearches(
recentSearches.slice(recentSearches.length - maxRecentSearchesLength)
);
} }
setRecentSearches((arr) => [ setRecentSearches((arr) => [
...arr, ...arr,
{ {
addressFrom: addressFrom.label, addressFrom: addressFrom.formatted_address,
addressTo: addressTo.label, addressTo: addressTo.formatted_address,
time: Date.now(), time: Date.now(),
}, },
]); ]);
await itinerariesService await searchItineraries({
.searchItineraries({ coordinatesFrom: addressFrom,
coordinatesFrom, coordinatesTo: addressTo,
coordinatesTo,
}) })
.then((response) => { .then((response) => {
// if (response.status === "error") { // if (response.status === "error") {
@@ -147,7 +121,18 @@ const BuscarItinerario: React.FC = () => {
// return; // return;
// } // }
setItinerariesList(response); history.push({
pathname: "/buscar/itinerario/lista",
state: {
coordinatesFrom,
coordinatesTo,
addressFrom,
addressTo,
itineraries: response,
},
});
// setItinerariesList(response);
}) })
.catch((err) => { .catch((err) => {
setToastColor("danger"); setToastColor("danger");
@@ -173,40 +158,20 @@ const BuscarItinerario: React.FC = () => {
<IonCardContent> <IonCardContent>
<div className="inputs-from-to"> <div className="inputs-from-to">
<IonIcon icon={locateOutline}></IonIcon> <IonIcon icon={locateOutline}></IonIcon>
{/* <IonSearchbar <AutoCompleteInput
showClearButton="never" placeholder="R. José Paulino, 1234"
onClick={() => setInputActiveOpenModal("from")} className="ml-2"
value={addressFrom} onAddressSelected={(address: Address) =>
placeholder="R. José Paulino, 1234 - Centro, Campinas - SP, 13013-001" setAddressFrom(address)
/> */} }
<GooglePlacesAutocomplete
apiKey={process.env.REACT_APP_KEY_API}
apiOptions={{ language: "pt-br", region: "br" }}
selectProps={{
value: addressFrom,
onChange: setAddressFrom,
className: "input-autocomplete",
placeholder: "R. José Paulino, 1234",
}}
/> />
</div> </div>
<div className="inputs-from-to"> <div className="inputs-from-to">
<IonIcon icon={locationOutline}></IonIcon> <IonIcon icon={locationOutline}></IonIcon>
{/* <IonSearchbar <AutoCompleteInput
showClearButton="never"
onClick={() => setInputActiveOpenModal("to")}
value={addressTo}
placeholder="PUC Campinas" placeholder="PUC Campinas"
/> */} className="ml-2"
<GooglePlacesAutocomplete onAddressSelected={(address: Address) => setAddressTo(address)}
apiKey={process.env.REACT_APP_KEY_API}
apiOptions={{ language: "pt-br", region: "br" }}
selectProps={{
value: addressTo,
onChange: setAddressTo,
className: "input-autocomplete",
placeholder: "PUC Campinas",
}}
/> />
</div> </div>
<div className="button-search"> <div className="button-search">
@@ -223,9 +188,9 @@ const BuscarItinerario: React.FC = () => {
<IonRow class="latest-searches"> <IonRow class="latest-searches">
{recentSearches.map((search, index) => { {recentSearches.map((search, index) => {
return ( return (
<> <div key={index}>
<div> <IonRow
<IonRow key={index}
class="latest-searches" class="latest-searches"
onClick={() => { onClick={() => {
fillSearchBars(search.addressFrom, search.addressTo); fillSearchBars(search.addressFrom, search.addressTo);
@@ -250,7 +215,6 @@ const BuscarItinerario: React.FC = () => {
></IonIcon> ></IonIcon>
</IonRow> </IonRow>
</div> </div>
</>
); );
})} })}
</IonRow> </IonRow>
@@ -258,102 +222,6 @@ const BuscarItinerario: React.FC = () => {
) : ( ) : (
<></> <></>
)} )}
{/* <IonRow class="latest-searches">
<IonIcon
className="icon-align-vcenter"
size="large"
icon={timeOutline}
/>
<div className="div_from_to">
<span>Taquaral</span>
<IonIcon icon={arrowForwardOutline}></IonIcon>
<span>PUC-Campinas</span>
<br />
<small>Há 2 hora</small>
</div>
<IonIcon
className="icon-forward icon-align-vcenter"
size="large"
icon={chevronForwardOutline}
/>
</IonRow> */}
{/* <IonModal isOpen={showModalEnd}>
<IonContent>
<div className="header-search-modal">
<IonIcon
className="icon-return-modal"
icon={arrowBack}
onClick={() => setShowModalEnd(false)}
/>
<IonInput
onIonChange={(e) => optionsAddress(e.detail.value)}
placeholder="R. José Paulino, 1234 - Centro, Campinas - SP, 13013-001"
className="search-modal"
/>
</div>
{addressResults.length > 0 ? (
addressResults.map((item: any) => {
return (
<div
key={item.value}
className="modal-search-results"
data-value={item.value}
data-label={item.label}
onClick={(e) => setAddress(e)}
>
{item.label}
<IonIcon
className="icon-results-modal"
icon={chevronForwardOutline}
/>
</div>
);
})
) : (
<>
<IonProgressBar type="indeterminate" />
<br />
</>
)}
</IonContent>
</IonModal> */}
{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 <IonToast
color={toastColor} color={toastColor}

View File

@@ -1,9 +1,9 @@
import { import {
IonContent, IonContent,
IonPage,
IonFab, IonFab,
IonFabButton, IonFabButton,
IonIcon, IonIcon,
IonPage,
} from "@ionic/react"; } from "@ionic/react";
import { search } from "ionicons/icons"; import { search } from "ionicons/icons";
import "./BuscarPassageiro.css"; import "./BuscarPassageiro.css";
@@ -12,11 +12,11 @@ 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 { PageHeader } from "../../components/PageHeader";
import { UserSearchInfos } from "../../components/UserSearchInfos/UserSearchInfos";
import { getUsersSearching } from "../../services/api/users";
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 { UserSearchInfos } from "../../components/UserSearchInfos/UserSearchInfos";
import { PageHeader } from "../../components/PageHeader";
const maptilerProvider = maptiler("d5JQJPLLuap8TkJJlTdJ", "streets"); const maptilerProvider = maptiler("d5JQJPLLuap8TkJJlTdJ", "streets");
@@ -52,7 +52,7 @@ const BuscarPassageiro: React.FC = () => {
const [results, setResults] = useState([]); const [results, setResults] = useState([]);
const [zoom, setZoom] = useState(14); const [zoom, setZoom] = useState(14);
const [moveMode, setMoveMode] = useState(false); // const [ moveMode, setMoveMode ] = useState(false);
// useEffect(() => { // useEffect(() => {

View File

@@ -1,55 +1,941 @@
import { Color } from "@ionic/core";
import { import {
IonBackButton, IonBackButton,
IonButton, IonButton,
IonButtons, IonButtons,
IonCheckbox,
IonContent, IonContent,
IonDatetime,
IonHeader, IonHeader,
IonIcon, IonIcon,
IonInput,
IonItem,
IonLabel,
IonList,
IonPage, IonPage,
IonSelect,
IonSelectOption,
IonSlide,
IonSlides,
IonTitle, IonTitle,
IonToast,
IonToolbar, IonToolbar,
} from "@ionic/react"; } from "@ionic/react";
import { close, locateOutline, locationOutline } from "ionicons/icons"; import {
import GooglePlacesAutocomplete from "react-google-places-autocomplete"; addCircleOutline,
import { PageHeader } from "../../components/PageHeader"; arrowBack,
arrowForward,
checkmark,
close,
informationCircle,
locateOutline,
locationOutline,
removeCircleOutline,
trash,
} from "ionicons/icons";
import { useEffect, useRef, useState } from "react";
import { useHistory } from "react-router";
import AutoCompleteInput from "../../components/AutoCompleteInput";
import { CreateItineraryRequest } from "../../services/api/itineraries";
import * as vehiclesRoutes from "../../services/api/vehicles";
import { createItinerary } from "../../services/functions/itinerariesService";
import sessionsService from "../../services/functions/sessionsService";
const slideOpts = {
initialSlide: 0,
allowTouchMove: false,
};
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;
}
interface Address {
formatted_address: string;
lat: number;
lng: number;
}
interface Destinations extends Address {
is_final?: boolean;
}
export default function CadastrarItinerario() { export default function CadastrarItinerario() {
const minDate = new Date();
const history = useHistory();
const [showToast, setShowToast] = useState<boolean>(false);
const [toastMessage, setToastMessage] = useState<string>("");
const [toastColor, setToastColor] = useState<Color>("primary");
const mySlides = useRef<any>(null);
const nextButton1 = useRef<HTMLIonButtonElement>(null);
const nextButton2 = useRef<HTMLIonButtonElement>(null);
const nextButton3 = useRef<HTMLIonButtonElement>(null);
const nextButton4 = useRef<HTMLIonButtonElement>(null);
const nextButton5 = useRef<HTMLIonButtonElement>(null);
const nextButton6 = useRef<HTMLIonButtonElement>(null);
const [specificDate, setSpecificDate] = useState<boolean>(false);
const [singleVacancy, setSingleVacancy] = useState<boolean>(false);
const [vans, setVans] = useState<VanInfo[]>();
//Infos
const [initialAddress, setInitialAddress] = useState<Address>();
const [neighborhoods, setNeighborhoods] = useState<Address[]>([]);
const [finalAddress, setFinalAddress] = useState<Address>();
const [destinations, setDestinations] = useState<Destinations[]>([]);
const [daysOfWeek, setDaysOfWeek] = useState<string>("0000000");
const [specificDay, setSpecificDay] = useState<string>("");
const [departureTime, setDepartureTime] = useState<string>("00:00");
const [arrivalTime, setArrivalTime] = useState<string>("00:00");
const [monthlyPrice, setMonthlyPrice] = useState<number>(100);
const [dailyPrice, setDailyPrice] = useState<number>(40);
const [van, setVan] = useState<string>("");
const [nickname, setNickname] = useState<string>("");
const redirectUserToLogin = () => {
history.push({ pathname: "/login" });
};
const onBtnClicked = async (direction: string) => {
const swiper = await mySlides.current.getSwiper();
if (direction === "next") {
swiper.slideNext();
} else if (direction === "prev") {
swiper.slidePrev();
}
};
function formatRange(rangeValue: number) {
switch (rangeValue) {
case 1:
return "Segunda";
case 2:
return "Terça";
case 3:
return "Quarta";
case 4:
return "Quinta";
case 5:
return "Sexta";
case 6:
return "Sabádo";
case 7:
return "Domingo";
default:
return "";
}
}
useEffect(() => {
const getUserVans = async () => {
let userId = "";
const refreshSessionRes = await sessionsService.refreshSession();
if (refreshSessionRes.error) {
redirectUserToLogin();
return;
}
if (refreshSessionRes.userId) {
userId = refreshSessionRes.userId;
}
await vehiclesRoutes
.getByUserId(userId)
.then((response: any) => {
if (response.status === "error") {
setToastColor("danger");
setToastMessage(response.message);
setShowToast(true);
return;
}
setVans(response.data);
})
.catch((err: any) => {
setToastColor("danger");
setToastMessage(err);
setShowToast(true);
});
};
getUserVans();
}, []);
useEffect(() => {
if (initialAddress) {
nextButton1.current!.disabled = false;
} else {
nextButton1.current!.disabled = true;
}
}, [initialAddress]);
useEffect(() => {
if (finalAddress) {
nextButton3.current!.disabled = false;
} else {
nextButton3.current!.disabled = true;
}
}, [finalAddress]);
useEffect(() => {
if (van) {
nextButton6.current!.disabled = false;
} else {
nextButton6.current!.disabled = true;
}
}, [van]);
useEffect(() => {
}, [destinations]);
function addNeighborhood(address: Address) {
setNeighborhoods((arr) => [...arr, address]);
nextButton2.current!.disabled = false;
}
function removeNeighborhood(index: number) {
const newNeighborhoods = [...neighborhoods];
newNeighborhoods.splice(index, 1);
setNeighborhoods(newNeighborhoods);
if (newNeighborhoods.length === 0) {
nextButton2.current!.disabled = true;
} else {
nextButton2.current!.disabled = false;
}
}
function removeDestionation(index: number) {
const newDestionations = [...destinations];
newDestionations.splice(index, 1);
setDestinations(newDestionations);
}
function setDayOfWeekSeleted(day: string, checked: boolean) {
switch (day) {
case "Domingo":
setDaysOfWeek((value) => {
if (checked) {
let newDaysOfWeek = value.split("");
newDaysOfWeek[0] = "1";
let finalString = newDaysOfWeek.join("");
return finalString;
} else {
let newDaysOfWeek = value.split("");
newDaysOfWeek[0] = "0";
let finalString = newDaysOfWeek.join("");
return finalString;
}
});
break;
case "Segunda":
setDaysOfWeek((value) => {
if (checked) {
let newDaysOfWeek = value.split("");
newDaysOfWeek[1] = "1";
let finalString = newDaysOfWeek.join("");
return finalString;
} else {
let newDaysOfWeek = value.split("");
newDaysOfWeek[1] = "0";
let finalString = newDaysOfWeek.join("");
return finalString;
}
});
break;
case "Terça":
setDaysOfWeek((value) => {
if (checked) {
let newDaysOfWeek = value.split("");
newDaysOfWeek[2] = "1";
let finalString = newDaysOfWeek.join("");
return finalString;
} else {
let newDaysOfWeek = value.split("");
newDaysOfWeek[2] = "0";
let finalString = newDaysOfWeek.join("");
return finalString;
}
});
break;
case "Quarta":
setDaysOfWeek((value) => {
if (checked) {
let newDaysOfWeek = value.split("");
newDaysOfWeek[3] = "1";
let finalString = newDaysOfWeek.join("");
return finalString;
} else {
let newDaysOfWeek = value.split("");
newDaysOfWeek[3] = "0";
let finalString = newDaysOfWeek.join("");
return finalString;
}
});
break;
case "Quinta":
setDaysOfWeek((value) => {
if (checked) {
let newDaysOfWeek = value.split("");
newDaysOfWeek[4] = "1";
let finalString = newDaysOfWeek.join("");
return finalString;
} else {
let newDaysOfWeek = value.split("");
newDaysOfWeek[4] = "0";
let finalString = newDaysOfWeek.join("");
return finalString;
}
});
break;
case "Sexta":
setDaysOfWeek((value) => {
if (checked) {
let newDaysOfWeek = value.split("");
newDaysOfWeek[5] = "1";
let finalString = newDaysOfWeek.join("");
return finalString;
} else {
let newDaysOfWeek = value.split("");
newDaysOfWeek[5] = "0";
let finalString = newDaysOfWeek.join("");
return finalString;
}
});
break;
case "Sábado":
setDaysOfWeek((value) => {
if (checked) {
let newDaysOfWeek = value.split("");
newDaysOfWeek[6] = "1";
let finalString = newDaysOfWeek.join("");
return finalString;
} else {
let newDaysOfWeek = value.split("");
newDaysOfWeek[6] = "0";
let finalString = newDaysOfWeek.join("");
return finalString;
}
});
break;
}
}
useEffect(() => {}, [daysOfWeek]);
async function cadastrar() {
let newDestinations: Destinations[] = [{...finalAddress!, is_final: true}];
newDestinations = newDestinations.concat(destinations);
let itinerary: CreateItineraryRequest = {
vehicle_plate: van,
days_of_week: daysOfWeek,
specific_day: specificDate ? specificDay : undefined,
estimated_departure_time: departureTime,
estimated_arrival_time: arrivalTime,
monthly_price: monthlyPrice,
daily_price: dailyPrice,
accept_daily: singleVacancy,
itinerary_nickname: nickname,
estimated_departure_address: initialAddress!.formatted_address,
departure_latitude: initialAddress!.lat,
departure_longitude: initialAddress!.lng,
neighborhoods_served: neighborhoods,
destinations: newDestinations,
};
await createItinerary(itinerary)
.then((response: any) => {
if (response.status === "error") {
setToastColor("danger");
setToastMessage(response.message);
setShowToast(true);
}
setToastColor("success");
setToastMessage(response.message);
setShowToast(true);
history.push({
pathname: "/meus-itinerarios",
// state: {
// redirectData: {
// showToastMessage: true,
// toastColor: "success",
// toastMessage: "Itinerário cadastrado com sucesso!",
// },
// },
});
})
.catch((err: any) => {
setToastColor("danger");
setToastMessage(err);
setShowToast(true);
});
}
return ( return (
<IonPage> <IonPage>
<PageHeader <IonHeader>
pageName="Cadastrar itinerário" <IonToolbar>
backButtonPageUrl="/perfil" <IonTitle>Cadastrar Itinerário</IonTitle>
></PageHeader> <IonButtons slot="start">
<IonBackButton icon={close} text="" defaultHref="/perfil" />
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent fullscreen> <IonContent fullscreen>
<IonSlides ref={mySlides} options={slideOpts}>
<IonSlide>
<div className="m-3"> <div className="m-3">
<h1>Digite o endereço de onde você iniciará a rota do itinerário</h1> <h1 className="mb-3 text-xl">
<div className="inputs-from-to"> Digite o endereço de onde você iniciará a rota do itinerário
</h1>
<div className="flex items-center mb-3">
<IonIcon icon={locateOutline}></IonIcon> <IonIcon icon={locateOutline}></IonIcon>
<GooglePlacesAutocomplete <AutoCompleteInput
apiKey={process.env.REACT_APP_KEY_API} placeholder="R. José Paulino, 1234"
apiOptions={{ language: "pt-br", region: "br" }} className="ml-2"
selectProps={{ onAddressSelected={(address: Address) =>
className: "input-autocomplete", setInitialAddress(address)
placeholder: "R. José Paulino, 1234", }
onChange={(e: any) => {
nextButton1.current!.disabled = true;
}} }}
/> />
</div> </div>
<div className="inputs-from-to"> <div className="flex justify-end mb-3">
<IonButton
ref={nextButton1}
disabled
onClick={() => onBtnClicked("next")}
color="primary"
>
<IonIcon icon={arrowForward} />
</IonButton>
</div>
<div className="flex items-center">
<IonIcon
icon={informationCircle}
size="large"
className="mr-4"
/>
<small className="text-gray-500">
Essa informação é importante para que possamos informar aos
passageiros um horário aproximado que você passará para
pegá-lo.
</small>
</div>
</div>
</IonSlide>
<IonSlide>
<div className="m-3">
<h1 className="mb-3 text-xl">
Adicione os bairros que você atenderá
</h1>
<div className="flex items-center mb-3">
<IonIcon icon={locationOutline}></IonIcon> <IonIcon icon={locationOutline}></IonIcon>
<GooglePlacesAutocomplete <AutoCompleteInput
apiKey={process.env.REACT_APP_KEY_API} placeholder="R. José Paulino, 1234"
apiOptions={{ language: "pt-br", region: "br" }} className="ml-2"
selectProps={{ onAddressSelected={(address: Address) =>
className: "input-autocomplete", addNeighborhood(address)
placeholder: "PUC Campinas", }
}}
/> />
</div> </div>
<div className="button-search"> <div className="mb-3">
<IonButton color="primary">Cadastrar</IonButton> <IonList class="w-screen">
{neighborhoods.map((neighborhood, index) => (
<IonItem key={index}>
<IonButton
slot="end"
color={"light"}
onClick={() => removeNeighborhood(index)}
>
<IonIcon icon={trash} />
</IonButton>
<IonLabel>{neighborhood.formatted_address}</IonLabel>
</IonItem>
))}
</IonList>
</div>
<div className="flex justify-between mb-3">
<IonButton onClick={() => onBtnClicked("prev")} color="primary">
<IonIcon icon={arrowBack} />
</IonButton>
<IonButton
ref={nextButton2}
disabled
onClick={() => onBtnClicked("next")}
color="primary"
>
<IonIcon icon={arrowForward} />
</IonButton>
</div>
<div className="flex items-center">
<IonIcon
icon={informationCircle}
size="large"
className="mr-4"
/>
<small className="text-gray-500">
Não se preocupe, você poderá adicionar ou remover bairros
posteriormente caso precise editando o itinerário.
</small>
</div> </div>
</div> </div>
</IonSlide>
<IonSlide>
<div className="m-3">
<h1 className="mb-3 text-xl">
Digite o endereço de destino final do itinerário
</h1>
<div className="flex items-center mb-3">
<IonIcon icon={locationOutline}></IonIcon>
<AutoCompleteInput
placeholder="R. José Paulino, 1234"
className="ml-2"
onAddressSelected={(address: Address) =>
setFinalAddress(address)
}
/>
</div>
<div className="flex justify-between mb-3">
<IonButton onClick={() => onBtnClicked("prev")} color="primary">
<IonIcon icon={arrowBack} />
</IonButton>
<IonButton
ref={nextButton3}
disabled
onClick={() => onBtnClicked("next")}
color="primary"
>
<IonIcon icon={arrowForward} />
</IonButton>
</div>
<div className="flex items-center">
<IonIcon
icon={informationCircle}
size="large"
className="mr-4"
/>
<small className="text-gray-500">
Não se preocupe, você poderá adicionar paradas.
</small>
</div>
</div>
</IonSlide>
<IonSlide>
<div className="m-3">
<h1 className="mb-3 text-xl">
Adicione paradas durante o trajeto do itinerário para encontrar
mais passageiros
</h1>
<div className="flex items-center mb-3">
<IonIcon icon={locationOutline}></IonIcon>
<AutoCompleteInput
placeholder="R. José Paulino, 1234"
className="ml-2"
onAddressSelected={(address: Address) =>
setDestinations((arr) => [...arr, address])
}
/>
</div>
<div className="mb-3">
<IonList class="w-screen">
{destinations.map((destination, index) => (
<IonItem key={index}>
<IonButton
slot="end"
color={"light"}
onClick={() => removeDestionation(index)}
>
<IonIcon icon={trash} />
</IonButton>
<IonLabel>{destination.formatted_address}</IonLabel>
</IonItem>
))}
</IonList>
</div>
<div className="flex justify-between mb-3">
<IonButton onClick={() => onBtnClicked("prev")} color="primary">
<IonIcon icon={arrowBack} />
</IonButton>
<IonButton onClick={() => onBtnClicked("next")} color="primary">
<IonIcon icon={arrowForward} />
</IonButton>
</div>
<div className="flex items-center">
<IonIcon
icon={informationCircle}
size="large"
className="mr-4"
/>
<small className="text-gray-500">
Não se preocupe, você poderá adicionar ou remover paradas
posteriormente caso precise editando o itinerário.
</small>
</div>
</div>
</IonSlide>
<IonSlide>
<div className="m-3">
<h1 className="mb-3 text-xl">
Escolha o(s) dia(s) da semana ou um dia específico que o
itinerário será realizado
</h1>
<div hidden={specificDate} className="mb-3">
<div className="grid grid-cols-7 gap-4">
<div>
<IonLabel>D</IonLabel>
</div>
<div>
<IonLabel>S</IonLabel>
</div>
<div>
<IonLabel>T</IonLabel>
</div>
<div>
<IonLabel>Q</IonLabel>
</div>
<div>
<IonLabel>Q</IonLabel>
</div>
<div>
<IonLabel>S</IonLabel>
</div>
<div>
<IonLabel>S</IonLabel>
</div>
</div>
<div className="grid grid-cols-7 gap-4">
<div>
<IonCheckbox
onIonChange={(e) =>
setDayOfWeekSeleted("Domingo", e.target.checked)
}
></IonCheckbox>
</div>
<div>
<IonCheckbox
onIonChange={(e) =>
setDayOfWeekSeleted("Segunda", e.target.checked)
}
></IonCheckbox>
</div>
<div>
<IonCheckbox
onIonChange={(e) =>
setDayOfWeekSeleted("Terça", e.target.checked)
}
></IonCheckbox>
</div>
<div>
<IonCheckbox
onIonChange={(e) =>
setDayOfWeekSeleted("Quarta", e.target.checked)
}
></IonCheckbox>
</div>
<div>
<IonCheckbox
onIonChange={(e) =>
setDayOfWeekSeleted("Quinta", e.target.checked)
}
></IonCheckbox>
</div>
<div>
<IonCheckbox
onIonChange={(e) =>
setDayOfWeekSeleted("Sexta", e.target.checked)
}
></IonCheckbox>
</div>
<div>
<IonCheckbox
onIonChange={(e) =>
setDayOfWeekSeleted("Sábado", e.target.checked)
}
></IonCheckbox>
</div>
</div>
</div>
<div className="mb-3">
<IonItem className="mb-3">
<IonLabel>Dia Específico ?</IonLabel>
<IonCheckbox
onIonChange={(event) =>
event.detail.checked
? setSpecificDate(true)
: setSpecificDate(false)
}
></IonCheckbox>
</IonItem>
<IonDatetime
min={minDate.toISOString()}
presentation="date"
hidden={!specificDate}
value={specificDay}
onIonChange={(e) =>
setSpecificDay(
typeof e.detail.value === "string" ? e.detail.value : ""
)
}
></IonDatetime>
</div>
<div className="flex justify-between mb-3">
<IonButton onClick={() => onBtnClicked("prev")} color="primary">
<IonIcon icon={arrowBack} />
</IonButton>
<IonButton onClick={() => onBtnClicked("next")} color="primary">
<IonIcon icon={arrowForward} />
</IonButton>
</div>
</div>
</IonSlide>
<IonSlide>
<div className="m-3">
<h1 className="mb-3 text-xl">
Qual o horário estimado de ínicio do itinerário ?
</h1>
<div className="mb-3">
<IonDatetime
presentation="time"
value={departureTime}
onIonChange={(event) =>
setDepartureTime(
typeof event.detail.value === "string"
? event.detail.value
: "00:00"
)
}
></IonDatetime>
</div>
<div className="flex justify-between mb-3">
<IonButton onClick={() => onBtnClicked("prev")} color="primary">
<IonIcon icon={arrowBack} />
</IonButton>
<IonButton onClick={() => onBtnClicked("next")} color="primary">
<IonIcon icon={arrowForward} />
</IonButton>
</div>
<div className="flex items-center">
<IonIcon
icon={informationCircle}
size="large"
className="mr-4"
/>
<small className="text-gray-500">
Não se preocupe, é apenas um horário estimado.
</small>
</div>
</div>
</IonSlide>
<IonSlide>
<div className="m-3">
<h1 className="mb-3 text-xl">
Qual o horário estimado de chegado no último destino do
itinerário ?
</h1>
<div className="mb-3">
<IonDatetime
presentation="time"
value={arrivalTime}
onIonChange={(event) =>
setArrivalTime(
typeof event.detail.value === "string"
? event.detail.value
: "00:00"
)
}
></IonDatetime>
</div>
<div className="flex justify-between mb-3">
<IonButton onClick={() => onBtnClicked("prev")} color="primary">
<IonIcon icon={arrowBack} />
</IonButton>
<IonButton onClick={() => onBtnClicked("next")} color="primary">
<IonIcon icon={arrowForward} />
</IonButton>
</div>
<div className="flex items-center">
<IonIcon
icon={informationCircle}
size="large"
className="mr-4"
/>
<small className="text-gray-500">
Não se preocupe, é apenas um horário estimado.
</small>
</div>
</div>
</IonSlide>
<IonSlide>
<div className="m-3">
<h1 className="mb-3 text-xl">
Preencha as informações de pagamento
</h1>
<IonLabel>Valor cobrado mensalmente</IonLabel>
<div className="flex justify-between items-center mb-3">
<IonIcon
size="large"
onClick={() => setMonthlyPrice(monthlyPrice - 1)}
icon={removeCircleOutline}
/>
<h1 className="text-xl">R$ {monthlyPrice}</h1>
<IonIcon
className="text-4xl"
size="large"
onClick={() => setMonthlyPrice(monthlyPrice + 1)}
icon={addCircleOutline}
/>
</div>
<div className="mb-3">
<IonItem className="mb-3">
<IonLabel>Aceitar pedidos para vagas avulsa ?</IonLabel>
<IonCheckbox
onIonChange={(event) =>
setSingleVacancy(event.detail.checked)
}
></IonCheckbox>
</IonItem>
<div hidden={!singleVacancy}>
<IonLabel>Valor cobrado por dia (vaga avulsa)</IonLabel>
<div className="flex justify-between items-center">
<IonIcon
size="large"
onClick={() => setDailyPrice(dailyPrice - 1)}
icon={removeCircleOutline}
/>
<h1 className="text-xl">R$ {dailyPrice}</h1>
<IonIcon
className="text-4xl"
size="large"
onClick={() => setDailyPrice(dailyPrice + 1)}
icon={addCircleOutline}
/>
</div>
</div>
</div>
<div className="flex justify-between mb-3">
<IonButton onClick={() => onBtnClicked("prev")} color="primary">
<IonIcon icon={arrowBack} />
</IonButton>
<IonButton onClick={() => onBtnClicked("next")} color="primary">
<IonIcon icon={arrowForward} />
</IonButton>
</div>
<div className="flex items-center">
<IonIcon
icon={informationCircle}
size="large"
className="mr-4"
/>
<small className="text-gray-500">
Lembre-se esse valor será cobrado independente do destino do
passageiro.
<br />
<b>
Em breve será possível cobrar valores diferentes para cada
destino.
</b>
</small>
</div>
</div>
</IonSlide>
<IonSlide>
<div className="m-3">
<h1 className="mb-3 text-xl">
Escolha o veículo que será utilizado no itinerário
</h1>
<div className="mb-3">
<IonSelect
interface="action-sheet"
placeholder="Selecione o veículo"
onIonChange={(event) => setVan(event.detail.value)}
className="w-screen"
>
{vans ? (
vans.map((van, index) => {
return (
<IonSelectOption key={index} value={van.plate}>
{van.brand +
" - " +
van.model +
" | Placa: " +
van.plate}
</IonSelectOption>
);
})
) : (
<></>
)}
</IonSelect>
</div>
<div className="flex justify-between mb-3">
<IonButton onClick={() => onBtnClicked("prev")} color="primary">
<IonIcon icon={arrowBack} />
</IonButton>
<IonButton
ref={nextButton6}
disabled
onClick={() => onBtnClicked("next")}
color="primary"
>
<IonIcon icon={arrowForward} />
</IonButton>
</div>
</div>
</IonSlide>
<IonSlide>
<div className="m-3">
<h1 className="mb-3 text-xl">
Escolha um apelido para o itinerário
</h1>
<div className="mb-3">
<IonInput
value={nickname}
onIonChange={(event) => setNickname(event.detail.value!)}
placeholder="Manhã - Centro"
></IonInput>
</div>
<div className="flex justify-between mb-3">
<IonButton onClick={() => onBtnClicked("prev")} color="primary">
<IonIcon icon={arrowBack} />
</IonButton>
<IonButton onClick={() => cadastrar()} color="primary">
<IonIcon icon={checkmark} />
Cadastrar
</IonButton>
</div>
</div>
</IonSlide>
</IonSlides>
<IonToast
position="top"
color={toastColor}
isOpen={showToast}
onDidDismiss={() => setShowToast(false)}
message={toastMessage}
duration={2500}
/>
</IonContent> </IonContent>
</IonPage> </IonPage>
); );

View File

@@ -1,16 +1,26 @@
import { IonToast, IonProgressBar, IonItem, IonLabel, IonInput, IonBackButton, IonButton, IonButtons, IonCardTitle, IonCol, IonContent, IonGrid, IonHeader, IonPage, IonRow, IonToolbar } from '@ionic/react'; import { Color } from "@ionic/core";
import { arrowBack, logoFacebook, mail } from 'ionicons/icons'; import {
import { Action } from '../../components/Action'; IonButton,
import { useContext, useEffect, useState } from 'react'; IonCardTitle,
import { useHistory, useParams } from 'react-router'; IonCol,
import './Cadastro.css'; IonContent,
import ModalExample from '../../components/Email'; IonGrid,
import * as UsersService from '../../services/api/users' IonInput,
import LocalStorage from '../../LocalStorage'; IonItem,
import { UserContext } from '../../App'; IonLabel,
import { Color } from '@ionic/core'; IonPage,
import { closeToast } from '../../services/utils'; IonRow,
import { PageHeader } from '../../components/PageHeader'; IonToast,
} from "@ionic/react";
import { useContext, useState } from "react";
import { useHistory, useParams } from "react-router";
import { UserContext } from "../../App";
import { Action } from "../../components/Action";
import { PageHeader } from "../../components/PageHeader";
import LocalStorage from "../../LocalStorage";
import * as UsersService from "../../services/api/users";
import { closeToast } from "../../services/utils";
import "./Cadastro.css";
const Cadastro: React.FC = () => { const Cadastro: React.FC = () => {
const history = useHistory(); const history = useHistory();
@@ -18,34 +28,35 @@ const Cadastro: React.FC = () => {
const user = useContext(UserContext); const user = useContext(UserContext);
const [showToast, setShowToast] = useState(false); const [showToast, setShowToast] = useState(false);
const [messageToast, setMessageToast] = useState(''); const [messageToast, setMessageToast] = useState("");
const [toastColor, setToastColor] = useState<Color>("primary"); const [toastColor, setToastColor] = useState<Color>("primary");
const [email, setEmail] = useState<string>(''); const [email, setEmail] = useState<string>("");
const [password, setPassword] = useState<string>(''); const [password, setPassword] = useState<string>("");
const [confirmPassword, setConfirmPassword] = useState<string>(''); const [confirmPassword, setConfirmPassword] = useState<string>("");
const [firstName, setFirstName] = useState<string>(''); const [firstName, setFirstName] = useState<string>("");
const [lastName, setLastName] = useState<string>(''); const [lastName, setLastName] = useState<string>("");
const [birthDate, setBirthDate] = useState<string>(''); const [birthDate, setBirthDate] = useState<string>("");
const [lResult, setlResult] = useState({ const [lResult, setlResult] = useState({
error: '', error: "",
success: true success: true,
}); });
const emailValidate = () => { const emailValidate = () => {
var usuario = email.substring(0, email.indexOf("@")); var usuario = email.substring(0, email.indexOf("@"));
var dominio = email.substring(email.indexOf("@") + 1, email.length); var dominio = email.substring(email.indexOf("@") + 1, email.length);
if ((usuario.length >= 1) && if (
(dominio.length >= 3) && usuario.length >= 1 &&
(usuario.search("@") == -1) && dominio.length >= 3 &&
(dominio.search("@") == -1) && usuario.search("@") === -1 &&
(usuario.search(" ") == -1) && dominio.search("@") === -1 &&
(dominio.search(" ") == -1) && usuario.search(" ") === -1 &&
(dominio.search(".") != -1) && dominio.search(" ") === -1 &&
(dominio.indexOf(".") >= 1) && dominio.search(".") !== -1 &&
(dominio.lastIndexOf(".") < dominio.length - 1)) dominio.indexOf(".") >= 1 &&
{ dominio.lastIndexOf(".") < dominio.length - 1
) {
return true; return true;
} else { } else {
return false; return false;
@@ -53,19 +64,20 @@ const Cadastro: React.FC = () => {
}; };
const clearResult = () => { const clearResult = () => {
lResult.error = ''; lResult.error = "";
lResult.success = true; lResult.success = true;
} };
const fieldValidate = async () => { const fieldValidate = async () => {
clearResult(); clearResult();
if (!emailValidate()) { if (!emailValidate()) {
lResult.error = 'E-mail inválido!'; lResult.error = "E-mail inválido!";
lResult.success = false; lResult.success = false;
return lResult; return lResult;
} else if(password.length < 7 || password.length > 12) { //TODO: validar de acordo com a documentação } else if (password.length < 7 || password.length > 12) {
lResult.error = 'A senha deve ter de 7 a 12 caracteres!'; //TODO: validar de acordo com a documentação
lResult.error = "A senha deve ter de 7 a 12 caracteres!";
lResult.success = false; lResult.success = false;
return lResult; return lResult;
} }
@@ -74,18 +86,24 @@ const Cadastro: React.FC = () => {
}; };
const handleSubmit = async () => { const handleSubmit = async () => {
if(name === '' || email === '' || birthDate === '' || password === '' || confirmPassword === '') { if (
setToastColor("warning") name === "" ||
setMessageToast('Nenhum campo pode estar vazio!'); email === "" ||
birthDate === "" ||
password === "" ||
confirmPassword === ""
) {
setToastColor("warning");
setMessageToast("Nenhum campo pode estar vazio!");
setShowToast(true); setShowToast(true);
return return;
} }
if (password !== confirmPassword) { if (password !== confirmPassword) {
setToastColor("warning") setToastColor("warning");
setMessageToast('As senhas devem ser iguais!'); setMessageToast("As senhas devem ser iguais!");
setShowToast(true); setShowToast(true);
return return;
} }
const signUpForm = { const signUpForm = {
@@ -93,47 +111,47 @@ const Cadastro: React.FC = () => {
lastname: lastName, lastname: lastName,
email: email, email: email,
birth_date: birthDate, birth_date: birthDate,
password: password password: password,
} };
let result = fieldValidate(); let result = fieldValidate();
if (!(await result).success) { if (!(await result).success) {
setToastColor("warning") setToastColor("warning");
setMessageToast(lResult.error); setMessageToast(lResult.error);
setShowToast(true); setShowToast(true);
return return;
} }
let retorno = await UsersService.create(signUpForm); let retorno = await UsersService.create(signUpForm);
if (!retorno.token) { if (!retorno.token) {
setToastColor('danger') setToastColor("danger");
setMessageToast(retorno.message); setMessageToast(retorno.message);
setShowToast(true); setShowToast(true);
return return;
} }
LocalStorage.setToken(retorno.token.token); LocalStorage.setToken(retorno.token.token);
user.setIsLoggedIn(true); user.setIsLoggedIn(true);
history.push({ pathname: '/home', state: { history.push({
pathname: "/home",
state: {
redirectData: { redirectData: {
showToastMessage: true, showToastMessage: true,
toastColor: "success", toastColor: "success",
toastMessage: "Usuário cadastrado com sucesso!", toastMessage: "Usuário cadastrado com sucesso!",
} },
}}) },
});
}; };
const { name } = useParams<{ name: string; }>(); const { name } = useParams<{ name: string }>();
return ( return (
<IonPage> <IonPage>
<PageHeader <PageHeader pageName="Cadastro" backButtonPageUrl="/login"></PageHeader>
pageName="Cadastro"
backButtonPageUrl="/login"
></PageHeader>
<IonContent fullscreen> <IonContent fullscreen>
<IonGrid className="ion-padding"> <IonGrid className="ion-padding">
@@ -145,69 +163,73 @@ const Cadastro: React.FC = () => {
</IonRow> </IonRow>
<IonRow> <IonRow>
<IonCol size="12"> <IonCol size="12">
<div id='nome-sobrenome'> <div id="nome-sobrenome">
<IonItem> <IonItem>
<IonLabel position='floating'>Nome</IonLabel> <IonLabel position="floating">Nome</IonLabel>
<IonInput <IonInput
type='text' type="text"
clearInput clearInput
onIonInput={(e: any) => setFirstName(e.target.value)} onIonInput={(e: any) => setFirstName(e.target.value)}
// error={isError} // error={isError}
// onIonChange={(e: any) => setFirstName(e.detail.value)} // onIonChange={(e: any) => setFirstName(e.detail.value)}
> ></IonInput>
</IonInput>
</IonItem> </IonItem>
<IonItem> <IonItem>
<IonLabel position='floating'>Sobrenome</IonLabel> <IonLabel position="floating">Sobrenome</IonLabel>
<IonInput <IonInput
clearInput clearInput
onIonChange={(e: any) => setLastName(e.target.value)} onIonChange={(e: any) => setLastName(e.target.value)}
> ></IonInput>
</IonInput>
</IonItem> </IonItem>
</div> </div>
<IonItem> <IonItem>
<IonLabel position='floating'>E-mail</IonLabel> <IonLabel position="floating">E-mail</IonLabel>
<IonInput <IonInput
clearInput clearInput
type='email' type="email"
onIonChange={(e: any) => setEmail(e.target.value)} onIonChange={(e: any) => setEmail(e.target.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"
onIonChange={(e: any) => setBirthDate(e.target.value)} onIonChange={(e: any) => setBirthDate(e.target.value)}
> ></IonInput>
</IonInput>
</IonItem> </IonItem>
<IonItem> <IonItem>
<IonLabel position='floating'>Senha</IonLabel> <IonLabel position="floating">Senha</IonLabel>
<IonInput <IonInput
clearInput clearInput
type='password' type="password"
onIonChange={(e: any) => setPassword(e.target.value)} onIonChange={(e: any) => setPassword(e.target.value)}
></IonInput> ></IonInput>
</IonItem> </IonItem>
<IonItem> <IonItem>
<IonLabel position='floating'>Confirme a senha</IonLabel> <IonLabel position="floating">Confirme a senha</IonLabel>
<IonInput <IonInput
clearInput clearInput
type='password' type="password"
onIonChange={(e: any) => setConfirmPassword(e.target.value)} onIonChange={(e: any) => setConfirmPassword(e.target.value)}
></IonInput> ></IonInput>
</IonItem> </IonItem>
<IonButton className="ion-margin-top" expand="block" onClick={ handleSubmit }>Cadastrar-se</IonButton> <IonButton
className="ion-margin-top"
expand="block"
onClick={handleSubmit}
>
Cadastrar-se
</IonButton>
</IonCol> </IonCol>
</IonRow> </IonRow>
<small className='ion-margin-top'> <small className="ion-margin-top">
Ao se cadastrar, você aceita nossos <a href="">Termos e Condições</a> e nossa <a href=""> Política de Privacidade</a>. Ao se cadastrar, você aceita nossos{" "}
<a href="">Termos e Condições</a> e nossa{" "}
<a href=""> Política de Privacidade</a>.
</small> </small>
<Action message="Já tem conta?" text="Login" link="/login" /> <Action message="Já tem conta?" text="Login" link="/login" />
</IonGrid> </IonGrid>

View File

@@ -1,28 +1,22 @@
import { import {
IonBackButton,
IonButtons,
IonCard, IonCard,
IonCardContent, IonCardContent,
IonContent, IonContent,
IonHeader,
IonIcon, IonIcon,
IonItem, IonItem,
IonLabel, IonLabel,
IonPage, IonPage,
IonTitle,
IonToast, IonToast,
IonToolbar
} from "@ionic/react"; } from "@ionic/react";
import React, { useEffect, useReducer, useState } from "react"; import React, { useEffect, useState } from "react";
import '../Perfil.css'
import { useHistory, useLocation } from "react-router";
import { callOutline, documentTextOutline } from "ionicons/icons"; import { callOutline, documentTextOutline } from "ionicons/icons";
import { useHistory, useLocation } from "react-router";
import "../Perfil.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"; import { PageHeader } from "../../components/PageHeader";
import { closeToast } from "../../services/utils";
interface cardItem { interface cardItem {
icon: string; icon: string;
@@ -50,57 +44,62 @@ interface LocationState {
showToastMessage: boolean; showToastMessage: boolean;
toastColor: Color; toastColor: Color;
toastMessage: string; toastMessage: string;
} };
} }
let items: cardItem[] = [ let items: cardItem[] = [
{ {
icon: documentTextOutline, icon: documentTextOutline,
label: 'Documento', label: "Documento",
description: 'Cadastre seu documento para que seu perfil possa ser verificado', description:
url: '/perfil/completar/documento', "Cadastre seu documento para que seu perfil possa ser verificado",
required: false url: "/perfil/completar/documento",
required: false,
}, },
{ {
icon: callOutline, icon: callOutline,
label: 'Informações de contato', label: "Informações de contato",
description: 'Cadastre seu número de telefone celular que para possam contatar você', description:
url: '/perfil/completar/telefone', "Cadastre seu número de telefone celular que para possam contatar você",
required: false url: "/perfil/completar/telefone",
} required: false,
] },
];
const CadastroCompletar: React.FC = () => { const CadastroCompletar: React.FC = () => {
const history = useHistory(); const history = useHistory();
const location = useLocation<LocationState>(); const location = useLocation<LocationState>();
const [showToast, setShowToast] = useState(false); const [showToast, setShowToast] = useState(false);
const [toastMessage, setToastMessage] = useState(''); const [toastMessage, setToastMessage] = useState("");
const [toastColor, setToastColor] = useState<Color>("primary"); const [toastColor, setToastColor] = useState<Color>("primary");
const handleCardClick = (item: cardItem) => { const handleCardClick = (item: cardItem) => {
if (!item.required) return if (!item.required) return;
history.push({ pathname: item.url, state: { userData: location.state.userData } }); history.push({
} pathname: item.url,
state: { userData: location.state.userData },
});
};
useEffect(() => { useEffect(() => {
if (!location.state || !location.state.userData) { if (!location.state || !location.state.userData) {
history.push({ pathname: '/perfil' }) history.push({ pathname: "/perfil" });
} }
if (location.state && location.state.redirectData) { if (location.state && location.state.redirectData) {
const redirectData = location.state.redirectData const redirectData = location.state.redirectData;
if (redirectData.showToastMessage) { if (redirectData.showToastMessage) {
setToastColor(redirectData.toastColor) setToastColor(redirectData.toastColor);
setToastMessage(redirectData.toastMessage) setToastMessage(redirectData.toastMessage);
setShowToast(true) setShowToast(true);
} }
} }
if (!location.state.userData.document) items[0].required = true if (!location.state.userData.document) items[0].required = true;
if (!location.state.userData.phone_number) items[1].required = true if (!location.state.userData.phone_number) items[1].required = true;
}, []); }, []);
return ( return (
@@ -113,7 +112,13 @@ const CadastroCompletar: React.FC = () => {
<IonContent> <IonContent>
{items.map((item, index) => { {items.map((item, index) => {
return ( return (
<IonCard button={item.required} key={index} onClick={() => { handleCardClick(item) }}> <IonCard
button={item.required}
key={index}
onClick={() => {
handleCardClick(item);
}}
>
<IonItem> <IonItem>
<IonIcon icon={item.icon} slot="start" /> <IonIcon icon={item.icon} slot="start" />
<IonLabel>{item.label}</IonLabel> <IonLabel>{item.label}</IonLabel>
@@ -121,7 +126,7 @@ const CadastroCompletar: React.FC = () => {
<IonCardContent>{item.description}</IonCardContent> <IonCardContent>{item.description}</IonCardContent>
</IonCard> </IonCard>
) );
})} })}
<IonToast <IonToast

View File

@@ -54,7 +54,7 @@ const Home: React.FC = () => {
// 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')
history.push(`/login`) history.push(`/login`)
}) })
} }

View File

@@ -70,9 +70,14 @@
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
margin: 1rem 0 5.5rem 0; /* margin: 1rem 0 5.5rem 0; */
margin-top: 20px;
} }
.msg-not-found{ .msg-not-found{
margin: 1.5rem; margin: 1.5rem;
} }
.msg-not-found span {
font-size: 120%;
}

View File

@@ -0,0 +1,259 @@
import {
IonContent,
IonPage,
IonFab,
IonFabButton,
IonIcon,
IonCard,
IonButton,
IonHeader,
IonToolbar,
IonLabel,
IonModal,
IonRadioGroup,
IonItem,
IonRadio,
IonCheckbox,
IonFooter,
IonToast,
IonCardHeader,
IonCardSubtitle,
IonCardTitle,
IonItemDivider,
IonCardContent,
IonChip,
IonGrid,
IonRow,
IonCol,
IonList,
IonListHeader,
IonTitle,
IonBackButton,
IonButtons,
} from "@ionic/react";
import {
arrowForwardOutline,
cashOutline,
closeOutline,
personOutline,
starOutline,
} from "ionicons/icons";
import { useEffect, useState } from "react";
import { useHistory, useLocation } from "react-router";
import { createUserSearch } from "../services/api/users";
import "./ListaItinerarios.css";
import { closeToast, convertNumberToPrice } from "../services/utils";
import { Itinerary } from "../models/itinerary.model";
import { PageHeader } from "../components/PageHeader";
interface InfoBusca {
addressFrom: any;
addressTo: any;
coordinatesFrom: any;
coordinatesTo: any;
itineraries: Itinerary[];
}
const ListaItinerarios: React.FC = () => {
const history = useHistory();
const location = useLocation();
const props = location.state as InfoBusca;
const [itinerariesList, setItinerariesList] = useState<Itinerary[]>([]);
const [showModalFilters, setShowModalFilters] = useState(true);
const [showToast, setShowToast] = useState(false);
const [messageToast, setMessageToast] = useState("");
const [toastColor, setToastColor] = useState("success");
useEffect(() => {
if (props.itineraries) {
setItinerariesList(props.itineraries);
}
}, [props]);
function criaAlerta() {
createUserSearch(
props.coordinatesFrom.lat,
props.coordinatesFrom.lng,
props.addressTo.label
)
.then(() => {
setMessageToast("Alerta criado com sucesso!");
setShowToast(true);
})
.catch((err: any) => {
setMessageToast("Não foi possível criar o alerta!");
setToastColor("danger");
setShowToast(true);
});
}
return (
<IonPage>
<PageHeader
pageName="Resultados da busca"
backButtonPageUrl="/buscar/itinerario"
/>
<IonContent fullscreen>
<IonCard color="light">
<IonCardHeader>
<IonCardSubtitle>Origem: {props.addressFrom.label}</IonCardSubtitle>
</IonCardHeader>
</IonCard>
<IonCard color="light">
<IonCardHeader>
<IonCardSubtitle>Destino: {props.addressTo.label}</IonCardSubtitle>
</IonCardHeader>
</IonCard>
{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>
<IonCardContent>
<p>
<IonIcon icon={personOutline} /> Vagas disponíveis:{" "}
{itinerary.available_seats}
</p>
<p>
<IonIcon icon={starOutline} /> Motorista:{" "}
{itinerary.price}
</p>
<p>
<IonIcon icon={cashOutline} /> Valor:{" "}
{convertNumberToPrice(itinerary.price)}
</p>
</IonCardContent>
</IonCardHeader>
</IonCard>
);
})}
</>
) : (
<>
<div className="msg-not-found">
<IonCard>
<IonCardContent>
<span>
Não foi encontrado nenhum itinerário que atenda essa rota.
</span>
</IonCardContent>
</IonCard>
<IonCard>
<IonCardContent>
<span>
Deseja criar um alerta para ser notificado caso haja
itinerário para essa origem e destino?
</span>
<div className="button-criar-alerta">
<IonButton onClick={() => criaAlerta()}>
Criar Alerta
</IonButton>
</div>
</IonCardContent>
</IonCard>
</div>
</>
)}
<IonFab
onClick={() => setShowModalFilters(true)}
vertical="bottom"
horizontal="center"
slot="fixed"
>
<IonFabButton>Filtros</IonFabButton>
</IonFab>
<IonModal isOpen={showModalFilters}>
<IonHeader translucent>
<IonToolbar>
<IonTitle>Filtros</IonTitle>
<IonButtons slot="start">
<IonIcon
size="large"
icon={closeOutline}
onClick={() => setShowModalFilters(false)}
/>
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent>
<IonList>
<IonListHeader>Ordenar por</IonListHeader>
<IonRadioGroup>
<IonItem>
<IonLabel>Sem filtro</IonLabel>
<IonRadio value="sem_filtro" />
</IonItem>
<IonItem>
<IonLabel>Menor preço</IonLabel>
<IonRadio value="menor_preco" />
</IonItem>
<IonItem>
<IonLabel>Avaliação</IonLabel>
<IonRadio value="avaliacao" />
</IonItem>
<IonItem>
<IonLabel>Lugares disponíveis</IonLabel>
<IonRadio value="lugares_disponiveis" />
</IonItem>
</IonRadioGroup>
</IonList>
<IonItemDivider />
<IonList>
<IonListHeader>Preferências</IonListHeader>
<IonItem>
<IonLabel>Vaga avulsa</IonLabel>
<IonCheckbox value="vaga_avulsa" />
</IonItem>
<IonItem>
<IonLabel>Ar condicionado</IonLabel>
<IonCheckbox value="ar_condicionado" />
</IonItem>
<IonItem>
<IonLabel>Assento preferencial</IonLabel>
<IonCheckbox value="assento_preferencial" />
</IonItem>
</IonList>
</IonContent>
<IonFooter>
<IonButton
expand="block"
onClick={() => setShowModalFilters(false)}
>
Aplicar Filtros
</IonButton>
</IonFooter>
</IonModal>
<IonToast
// cssClass={"toast-notification"}
color={toastColor}
isOpen={showToast}
onDidDismiss={() => closeToast(setShowToast)}
message={messageToast}
duration={2500}
/>
</IonContent>
</IonPage>
);
};
export default ListaItinerarios;

View File

@@ -105,7 +105,7 @@ const Page: React.FC = () => {
// 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");
}); });
}; };

View File

@@ -15,82 +15,46 @@ import {
IonToolbar, IonToolbar,
} 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 { useEffect, useState } from "react";
import { getItineraries } from "../../services/api/itineraries";
import { PageHeader } from "../../components/PageHeader"; import { PageHeader } from "../../components/PageHeader";
import "./MeusItinerarios.css"; import "./MeusItinerarios.css";
interface Address {
formatted_address: string;
lat: number;
lng: number;
}
interface Destination extends Address {
is_final?: boolean;
}
interface ItineraryInfo { interface ItineraryInfo {
id_itinerary: number; id_itinerary: number;
vehicle_plate: string; vehicle_plate: string;
days_of_week: number; days_of_week?: string;
specific_day: string; specific_day?: string;
estimated_departure_time: string; estimated_departure_time: string;
estimated_arrival_time: string; estimated_arrival_time: string;
available_seats: number; monthly_price: number;
price: number; daily_price?: number;
accept_daily: boolean;
itinerary_nickname: string; itinerary_nickname: string;
estimated_departure_address: string;
departure_latitude: number;
departure_longitude: number;
neighborhoods_served: Address[];
destinations: Destination[];
} }
export default function MeusItinerarios() { export default function MeusItinerarios() {
const [routes, setRoutes] = useState<ItineraryInfo[]>( const [routes, setRoutes] = useState<ItineraryInfo[]>([]);
[
{ useEffect(() => {
id_itinerary: 1, getItineraries().then((response) => {
vehicle_plate: 'FSS1918', setRoutes(response.data);
days_of_week: 3, });
specific_day: '24/08/2022', }, [])
estimated_departure_time: '10:00',
estimated_arrival_time: '12:00',
available_seats: 20,
price: 108.20,
itinerary_nickname: 'Itinerário teste',
},
{
id_itinerary: 1,
vehicle_plate: 'FSS1918',
days_of_week: 3,
specific_day: '24/08/2022',
estimated_departure_time: '10:00',
estimated_arrival_time: '12:00',
available_seats: 20,
price: 108.20,
itinerary_nickname: 'Itinerário teste 2',
},
{
id_itinerary: 1,
vehicle_plate: 'FSS1918',
days_of_week: 3,
specific_day: '24/08/2022',
estimated_departure_time: '10:00',
estimated_arrival_time: '12:00',
available_seats: 20,
price: 108.20,
itinerary_nickname: 'Itinerário teste',
},
{
id_itinerary: 1,
vehicle_plate: 'FSS1918',
days_of_week: 3,
specific_day: '24/08/2022',
estimated_departure_time: '10:00',
estimated_arrival_time: '12:00',
available_seats: 20,
price: 108.20,
itinerary_nickname: 'Itinerário teste',
},
{
id_itinerary: 1,
vehicle_plate: 'FSS1918',
days_of_week: 3,
specific_day: '24/08/2022',
estimated_departure_time: '10:00',
estimated_arrival_time: '12:00',
available_seats: 20,
price: 108.20,
itinerary_nickname: 'Itinerário teste',
},
]
);
return ( return (
<IonPage> <IonPage>
@@ -109,15 +73,23 @@ export default function MeusItinerarios() {
</IonCardHeader> </IonCardHeader>
<IonCardContent> <IonCardContent>
<div className="addresses-itinerary"> <div className="addresses-itinerary">
<IonIcon icon={locateOutline}></IonIcon> <IonIcon icon={locateOutline} className="mr-1"></IonIcon>
Rua Francisco Glicerio, 100, Vila Novam aaaaaaaaaaaaaaaaaaaaaaaaaaa {itinerary.estimated_departure_address}
</div> </div>
<div className="icons-location-divider"> <div className="icons-location-divider">
| |
</div> </div>
<div className="addresses-itinerary"> <div className="addresses-itinerary">
<IonIcon icon={locationOutline}></IonIcon> <IonIcon icon={locationOutline} className="mr-1"></IonIcon>
PUC Campinas H15 Campus 1 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa {itinerary.destinations.map((destination) => {
if (destination.is_final) {
return (
<>
{destination.formatted_address}
</>
)
}
})}
</div> </div>
</IonCardContent> </IonCardContent>
</IonCard> </IonCard>

View File

@@ -1,26 +1,19 @@
import { import {
IonBackButton,
IonBadge, IonBadge,
IonButtons,
IonCard, IonCard,
IonCardContent, IonCardContent,
IonCardHeader, IonCardHeader,
IonCardTitle, IonCardTitle,
IonChip, IonChip,
IonContent, IonContent,
IonHeader,
IonIcon, IonIcon,
IonItem, IonItem,
IonLabel, IonLabel,
IonList, IonList,
IonListHeader, IonListHeader,
IonPage, IonPage,
IonTitle,
IonToast, IonToast,
IonToolbar,
} from "@ionic/react"; } from "@ionic/react";
import { useHistory, useLocation } from "react-router-dom";
import React, { useState, useEffect, useReducer, useContext } from "react";
import { import {
callOutline, callOutline,
cardOutline, cardOutline,
@@ -32,16 +25,21 @@ import {
shieldCheckmarkOutline, shieldCheckmarkOutline,
starOutline, starOutline,
} from "ionicons/icons"; } from "ionicons/icons";
import React, { useContext, useEffect, useReducer, useState } from "react";
import { useHistory, useLocation } from "react-router-dom";
import "./Perfil.css";
import LocalStorage from "../LocalStorage"; import LocalStorage from "../LocalStorage";
import "./Perfil.css";
import sessionsService from "../services/functions/sessionsService";
import usersService from "../services/functions/usersService";
import { UserContext } from "../App";
import { Color } from "@ionic/core"; import { Color } from "@ionic/core";
import { closeToast } from "../services/utils"; import { UserContext } from "../App";
import { PageHeader } from "../components/PageHeader"; import { PageHeader } from "../components/PageHeader";
import sessionsService from "../services/functions/sessionsService";
import {
checkIfUserIsDriver,
getById,
} from "../services/functions/usersService";
import { closeToast } from "../services/utils";
interface ScanNewProps { interface ScanNewProps {
match: { match: {
@@ -133,7 +131,7 @@ const Perfil: React.FC<ScanNewProps> = (props) => {
} }
// get user info by ID // get user info by ID
const getByIdRes = await usersService.getById(userId); const getByIdRes = await getById(userId);
if (getByIdRes.error) { if (getByIdRes.error) {
if (isVisitor && props.match.params.id) { if (isVisitor && props.match.params.id) {
@@ -148,8 +146,8 @@ const Perfil: React.FC<ScanNewProps> = (props) => {
return; return;
} }
// check if user is driver (if they have vehicles) // check if user is driver (if they have vans)
const userIsDriverRes = await usersService.checkIfUserIsDriver(userId); const userIsDriverRes = await checkIfUserIsDriver(userId);
// if (userIsDriverRes.error) { // if (userIsDriverRes.error) {
// setToastColor('warning') // setToastColor('warning')
@@ -347,6 +345,15 @@ const Perfil: React.FC<ScanNewProps> = (props) => {
<IonIcon icon={personOutline} slot="start" /> <IonIcon icon={personOutline} slot="start" />
<IonLabel>Buscar passageiros</IonLabel> <IonLabel>Buscar passageiros</IonLabel>
</IonItem> </IonItem>
<IonItem
button
onClick={() =>
history.push({ pathname: "/cadastrar-itinerario" })
}
>
<IonIcon icon={mapOutline} slot="start" />
<IonLabel>Cadastrar itinerário</IonLabel>
</IonItem>
<IonItem <IonItem
button button
onClick={() => onClick={() =>

View File

@@ -1,224 +0,0 @@
import {
IonContent,
IonPage,
IonFab,
IonFabButton,
IonIcon,
IonCard,
IonInput,
IonRow,
IonCol,
IonCardContent,
IonButton,
IonHeader,
IonToolbar,
IonButtons,
IonBackButton,
IonTabs,
IonTabBar,
IonTabButton,
IonLabel,
IonBadge,
IonRouterOutlet,
IonSlides,
IonSlide,
IonModal,
IonList,
IonRadioGroup,
IonListHeader,
IonItem,
IonRadio,
IonCheckbox,
IonFooter,
IonToast,
} from "@ionic/react";
import {
arrowBack,
arrowBackOutline,
arrowForwardOutline,
chevronBackOutline,
chevronForwardOutline,
closeOutline,
locateOutline,
locationOutline,
timeOutline,
} from "ionicons/icons";
import { useEffect, useState } from "react";
import { useHistory, useLocation } from "react-router";
import itinerariesService from "../../services/functions/itinerariesService";
import { createUserSearch } from "../../services/api/users";
import "./Transportes.css";
import { closeToast } from "../../services/utils";
interface InfoBusca {
addressFrom: any;
addressTo: any;
coordinatesFrom: any;
coordinatesTo: any;
}
const Transportes: React.FC = () => {
const history = useHistory();
const location = useLocation();
const props = location.state as InfoBusca;
const [itinerarios, setItinerarios] = useState([]);
const [showModalFilters, setShowModalFilters] = useState(false);
const [showToast, setShowToast] = useState(false);
const [messageToast, setMessageToast ] = useState('');
const [toastColor, setToastColor] = useState('success');
useEffect(() => {
if (props) {
buscaItinerarios();
}
}, [props]);
async function buscaItinerarios() {
let data = (await itinerariesService.searchItineraries(props)) as any;
setItinerarios(data);
}
function criaAlerta(){
createUserSearch(props.coordinatesFrom.lat, props.coordinatesFrom.lng, props.addressTo.label).then(() => {
setMessageToast('Alerta criado com sucesso!');
setShowToast(true);
}).catch((err:any) => {
setMessageToast('Não foi possível criar o alerta!');
setToastColor('danger');
setShowToast(true);
})
}
return (
<IonPage>
{/* TODO, componentizar Header */}
<IonHeader>
<div className="header-page">
{/* <IonButtons slot="start">
<IonBackButton text={'aaaa'} icon={arrowBack} defaultHref='buscar-transporte' />
</IonButtons> */}
<span className="span-info-back" onClick={history.goBack}>
<IonIcon className="icon-return" icon={chevronBackOutline} />
<div className="address-from-to">
<span>{props.addressFrom.label}</span>
<IonIcon icon={arrowForwardOutline} />
<span>{props.addressTo.label}</span>
<small>Hoje</small>
</div>
</span>
</div>
</IonHeader>
<IonContent fullscreen>
{itinerarios && itinerarios.length > 0? (
<div className="header-tabs">
<IonSlides>
<IonSlide>
<h5>Mais barata</h5>
<IonCard className="card-transporte">
<IonCardContent>Seu João</IonCardContent>
</IonCard>
</IonSlide>
<IonSlide>
<h5>Melhor avaliação</h5>
<IonCard className="card-transporte">
<IonCardContent>Seu </IonCardContent>
</IonCard>
</IonSlide>
</IonSlides>
</div>
)
:
(<h1 className="msg-not-found">Não foi encontrado nenhum transporte que atenda essa rota.</h1>)}
{itinerarios &&
itinerarios.map((record: any, index: any) => {
return (
<IonCard className="card-transporte" key={index}>
<IonCardContent>
<h1>Motorista: {record.motorista}</h1>
<div>Avaliação: {record.avaliacao}</div>
<div>Valor: {record.valor}</div>
<div>Lugares disponíveis: {record.lugares}</div>
</IonCardContent>
</IonCard>
);
})}
<div className="button-criar-alerta">
<IonButton onClick={() => criaAlerta()}>Criar Alerta</IonButton>
</div>
<IonFab
onClick={() => setShowModalFilters(true)}
vertical="bottom"
horizontal="center"
slot="fixed"
>
<IonFabButton>Filtros</IonFabButton>
</IonFab>
<IonModal isOpen={showModalFilters}>
<IonToolbar>
<div className="header-filter-modal">
<IonIcon
size="large"
icon={closeOutline}
onClick={() => setShowModalFilters(false)}
/>
<h4>
<b>Limpar</b>
</h4>
</div>
</IonToolbar>
<IonContent>
<div className="content-filter-modal">
<h1>Filtrar</h1>
<h3>Ordernar por</h3>
<IonRadioGroup>
<IonItem>
<IonLabel>Menor preço</IonLabel>
<IonRadio value="menor_preco" />
</IonItem>
<IonItem>
<IonLabel>Avaliação</IonLabel>
<IonRadio value="avaliacao" />
</IonItem>
<IonItem>
<IonLabel>Lugares disponíveis</IonLabel>
<IonRadio value="lugares_disponiveis" />
</IonItem>
</IonRadioGroup>
<h3>Preferências</h3>
<IonItem>
<IonLabel>Vaga avulsa</IonLabel>
<IonCheckbox value="vaga_avulsa" />
</IonItem>
<IonItem>
<IonLabel>Ar condicionado</IonLabel>
<IonCheckbox value="ar_condicionado" />
</IonItem>
</div>
</IonContent>
<IonFooter>
<IonButton
expand="block"
onClick={() => setShowModalFilters(false)}
>
Aplicar Filtros
</IonButton>
</IonFooter>
</IonModal>
<IonToast
// cssClass={"toast-notification"}
color={toastColor}
isOpen={showToast}
onDidDismiss={() => closeToast(setShowToast)}
message={messageToast}
duration={2500}
/>
</IonContent>
</IonPage>
);
};
export default Transportes;

View File

@@ -1,22 +1,16 @@
import { import {
IonToast,
IonItem,
IonLabel,
IonInput,
IonBackButton,
IonButton, IonButton,
IonButtons,
IonContent,
IonHeader,
IonPage,
IonToolbar,
IonTitle,
IonList,
IonCheckbox, IonCheckbox,
IonListHeader, IonContent,
IonInput,
IonItem,
IonItemDivider,
IonLabel,
IonList,
IonPage,
IonSelect, IonSelect,
IonSelectOption, IonSelectOption,
IonItemDivider, IonToast,
} from "@ionic/react"; } from "@ionic/react";
import React, { useEffect, useReducer, useState } from "react"; import React, { useEffect, useReducer, useState } from "react";
@@ -28,10 +22,10 @@ import carsService from "../services/functions/carsService";
import * as vehiclesRoutes from "../services/api/vehicles"; import * as vehiclesRoutes from "../services/api/vehicles";
import "./VeiculoCadastro.css";
import { Color } from "@ionic/core"; import { Color } from "@ionic/core";
import { closeToast } from "../services/utils";
import { PageHeader } from "../components/PageHeader"; import { PageHeader } from "../components/PageHeader";
import { closeToast } from "../services/utils";
import "./VeiculoCadastro.css";
const VeiculoCadastro: React.FC = () => { const VeiculoCadastro: React.FC = () => {
const history = useHistory(); const history = useHistory();
@@ -209,7 +203,7 @@ const VeiculoCadastro: React.FC = () => {
} }
history.push({ history.push({
pathname: "/minhas-vehicles", pathname: "/veiculos/meus",
state: { state: {
redirectData: { redirectData: {
showToastMessage: true, showToastMessage: true,
@@ -262,7 +256,9 @@ const VeiculoCadastro: React.FC = () => {
<IonContent> <IonContent>
<IonList lines="full" class="ion-no-margin"> <IonList lines="full" class="ion-no-margin">
<IonItemDivider color={"primary"}>Informações do veículo</IonItemDivider> <IonItemDivider color={"primary"}>
Informações do veículo
</IonItemDivider>
<IonItem> <IonItem>
<IonLabel position="fixed">Placa </IonLabel> <IonLabel position="fixed">Placa </IonLabel>
<IonInput <IonInput

View File

@@ -1,10 +1,9 @@
import instance from './api'; import instance from "./api";
// import LocalStorage from '../LocalStorage'; // import LocalStorage from '../LocalStorage';
import transportsRoutes from '../../constants/routes/itinerariesRoutes'; import { AxiosRequestHeaders } from "axios";
import { AxiosRequestHeaders } from 'axios'; import transportsRoutes from "../../constants/routes/itinerariesRoutes";
import LocalStorage from '../../LocalStorage'; import LocalStorage from "../../LocalStorage";
import { setStore } from '../../store/RecordsStore';
let token: string; let token: string;
let header: AxiosRequestHeaders; let header: AxiosRequestHeaders;
@@ -13,27 +12,68 @@ function updateHeader() {
token = LocalStorage.getToken(); token = LocalStorage.getToken();
header = { header = {
"Accept": 'application/json', Accept: "application/json",
"Content-Type": 'application/json', "Content-Type": "application/json",
"Authorization": 'Bearer ' + token Authorization: "Bearer " + token,
} };
} }
export interface Coordinates { export interface Coordinates {
lat: number, lat: number;
lng: number lng: number;
} }
export async function get() { interface Address {
formatted_address: string;
lat: number;
lng: number;
}
export interface CreateItineraryRequest {
vehicle_plate: string;
days_of_week?: string;
specific_day?: string;
estimated_departure_time: string;
estimated_arrival_time: string;
monthly_price: number;
daily_price?: number;
accept_daily: boolean;
itinerary_nickname: string;
estimated_departure_address: string;
departure_latitude: number;
departure_longitude: number;
neighborhoods_served: Array<Address>;
destinations: Array<Address>;
}
export async function getItineraries() {
updateHeader(); updateHeader();
const response = await instance.get(transportsRoutes.get.url, { headers: header }); const response = await instance.get(transportsRoutes.get.url, {
headers: header,
});
return response.data; return response.data;
} }
export async function search(coordinatesOrigin: Coordinates, coordinatesDestination: Coordinates) { export async function create(itinerary: CreateItineraryRequest) {
updateHeader(); updateHeader();
const response = await instance.post(transportsRoutes.search.url, { coordinatesOrigin, coordinatesDestination }, { headers: header }); const response = await instance.post(transportsRoutes.create.url, itinerary, {
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; return response.data;
} }

View File

@@ -1,10 +1,10 @@
import instance from './api'; import instance from "./api";
// import LocalStorage from '../LocalStorage'; // import LocalStorage from '../LocalStorage';
import userRoutes from '../../constants/routes/usersRoutes'; import { AxiosRequestHeaders } from "axios";
import { AxiosRequestHeaders } from 'axios'; import userRoutes from "../../constants/routes/usersRoutes";
import LocalStorage from '../../LocalStorage'; import LocalStorage from "../../LocalStorage";
import { setStore } from '../../store/RecordsStore'; import { setStore } from "../../store/RecordsStore";
let token: string; let token: string;
let header: AxiosRequestHeaders; let header: AxiosRequestHeaders;
@@ -13,10 +13,10 @@ function updateHeader() {
token = LocalStorage.getToken(); token = LocalStorage.getToken();
header = { header = {
"Accept": 'application/json', Accept: "application/json",
"Content-Type": 'application/json', "Content-Type": "application/json",
"Authorization": 'Bearer ' + token Authorization: "Bearer " + token,
} };
} }
export interface CadastroResponse { export interface CadastroResponse {
@@ -63,21 +63,28 @@ export async function create(CadastroRequest: any) {
export async function getById(userId: string) { export async function getById(userId: string) {
updateHeader(); updateHeader();
const response = await instance.get(userRoutes.get.url + `/${userId}`, { headers: header }); const response = await instance.get(userRoutes.get.url + `/${userId}`, {
headers: header,
});
return response.data; return response.data;
} }
export async function update(userData: UpdateUserRequest) { export async function update(userData: UpdateUserRequest) {
updateHeader(); updateHeader();
const response = await instance.patch(userRoutes.update.url, userData, { headers: header }); const response = await instance.patch(userRoutes.update.url, userData, {
headers: header,
});
return response.data; return response.data;
} }
export async function checkIfUserIsDriver(id_user: string) { export async function checkIfUserIsDriver(id_user: string) {
updateHeader(); updateHeader();
const response = await instance.get(userRoutes.checkIfUserIsDriver.url + `/${id_user}`, { headers: header }); const response = await instance.get(
userRoutes.checkIfUserIsDriver.url + `/${id_user}`,
{ headers: header }
);
return response.data; return response.data;
} }
@@ -85,7 +92,10 @@ export async function checkIfUserIsDriver(id_user: string) {
export async function getSocialInfo(userId: string) { export async function getSocialInfo(userId: string) {
updateHeader(); updateHeader();
const response = await instance.get(userRoutes.getSocialInfo.url + `/${userId}`, { headers: header }); const response = await instance.get(
userRoutes.getSocialInfo.url + `/${userId}`,
{ headers: header }
);
return response.data; return response.data;
} }
@@ -93,17 +103,28 @@ export async function getUsersSearching(currentPoint: any) {
// Replace lat/long with values from get current location. // Replace lat/long with values from get current location.
// Allow choosing of radius? // Allow choosing of radius?
// Offset could = amount loaded in an infinite scroll? // Offset could = amount loaded in an infinite scroll?
var latitude = currentPoint.latitude, longitude = currentPoint.longitude, radius = 3000, offset = 0; // var latitude = currentPoint.latitude, longitude = currentPoint.longitude, radius = 3000, offset = 0;
// const response = await fetch(`http://localhost:4000/get-records?latitude=${ latitude }&longitude=${ longitude }&radius=${ radius }&offset=${ offset }`); // const response = await fetch(`http://localhost:4000/get-records?latitude=${ latitude }&longitude=${ longitude }&radius=${ radius }&offset=${ offset }`);
const response = await instance.post(`${userRoutes.getUsersSearching.url}`, currentPoint) const response = await instance.post(
`${userRoutes.getUsersSearching.url}`,
currentPoint
);
// const data = await response.json(); // const data = await response.json();
console.log(response.data) console.log(response.data);
setStore(response.data); setStore(response.data);
} }
export async function createUserSearch(latitude_from: any, longitude_from: any, addres_to: any) { export async function createUserSearch(
const response = await instance.post(`${userRoutes.createUserSearch.url}`, { latitude_from, longitude_from, addres_to }); latitude_from: any,
longitude_from: any,
addres_to: any
) {
const response = await instance.post(`${userRoutes.createUserSearch.url}`, {
latitude_from,
longitude_from,
addres_to,
});
console.log(response) console.log(response);
setStore(response); setStore(response);
} }

View File

@@ -10,13 +10,13 @@ interface getAllCarBrandsReturn {
error?: { error?: {
errorMessage: string; errorMessage: string;
} };
} }
interface getAllCarBrandsRes { interface getAllCarBrandsRes {
status?: string; status?: string;
message: string message: string;
data?: CarObject[]; data?: CarObject[];
} }
@@ -45,7 +45,9 @@ const getAllCarBrands = async (): Promise<getAllCarBrandsReturn> => {
} }
}; };
const getCarModels = async (carBrandId: string): Promise<getAllCarBrandsReturn> => { const getCarModels = async (
carBrandId: string
): Promise<getAllCarBrandsReturn> => {
try { try {
let res: getAllCarBrandsRes = await carsRoutes.listCarModels(carBrandId); let res: getAllCarBrandsRes = await carsRoutes.listCarModels(carBrandId);

View File

@@ -1,38 +1,50 @@
import * as itinerariesRoutes from '../api/itineraries'; import * as itinerariesRoutes from "../api/itineraries";
interface CoordinatesRequest { interface CoordinatesRequest {
coordinatesFrom: { coordinatesFrom: {
lat: number, formatted_address: string;
lng: number lat: number;
}, lng: number;
};
coordinatesTo: { coordinatesTo: {
lat: number, formatted_address: string;
lng: number lat: number;
} lng: number;
};
} }
export async function getAllItineraries(): Promise<any> { export async function getAllItineraries(): Promise<any> {
let res: any; let res: any;
try { try {
res = await itinerariesRoutes.get(); res = await itinerariesRoutes.getItineraries();
} catch (error) { } catch (error) {
// TODO // TODO
} }
return res.data return res.data;
} }
export async function searchItineraries({
export async function searchItineraries({ coordinatesFrom, coordinatesTo }: CoordinatesRequest): Promise<any> { coordinatesFrom,
let res: any coordinatesTo,
}: CoordinatesRequest): Promise<any> {
let res: any;
try { try {
res = await itinerariesRoutes.search(coordinatesFrom, coordinatesTo); res = await itinerariesRoutes.search(coordinatesFrom, coordinatesTo);
} catch (error) { } catch (error) {
// TODO // TODO
} }
return res.data return res.data;
} }
export default { getAllItineraries, searchItineraries } export async function createItinerary(
itinerary: itinerariesRoutes.CreateItineraryRequest
): Promise<any> {
let res: any;
res = await itinerariesRoutes.create(itinerary);
return res;
}

View File

@@ -14,7 +14,7 @@ interface refreshSessionResponse {
const refreshSession = async (): Promise<refreshSessionReturn> => { const refreshSession = async (): Promise<refreshSessionReturn> => {
try { try {
let res: refreshSessionResponse = await sessionRoutes.refresh() let res: refreshSessionResponse = await sessionRoutes.refresh();
if (res.status === "error") { if (res.status === "error") {
return { return {
@@ -43,4 +43,8 @@ const refreshSession = async (): Promise<refreshSessionReturn> => {
// } // }
}; };
export default { refreshSession } const method = {
refreshSession,
};
export default method;

View File

@@ -10,10 +10,10 @@ interface getByIdReturn {
bio: string; bio: string;
document_type: string; document_type: string;
document: string; document: string;
}, };
error?: { error?: {
errorMessage: string; errorMessage: string;
} };
} }
interface getByIdRes { interface getByIdRes {
@@ -29,18 +29,18 @@ interface getByIdRes {
bio: string; bio: string;
document_type: string; document_type: string;
document: string; document: string;
}, };
} }
const getById = async (userId: string): Promise<getByIdReturn> => { export const getById = async (userId: string): Promise<getByIdReturn> => {
try { try {
let res: getByIdRes = await usersRoutes.getById(userId) let res: getByIdRes = await usersRoutes.getById(userId);
if (res.status === "error") { if (res.status === "error") {
return { return {
error: { error: {
errorMessage: res.message, errorMessage: res.message,
} },
}; };
} }
@@ -51,32 +51,34 @@ const getById = async (userId: string): Promise<getByIdReturn> => {
return { return {
error: { error: {
errorMessage: "Por favor, autentique-se.", errorMessage: "Por favor, autentique-se.",
} },
}; };
} }
}; };
interface getByIdReturn { interface getByIdReturn {
data?: { data?: {
phone: '', phone: "";
whatsapp: '', whatsapp: "";
facebook: '', facebook: "";
telegram: '', telegram: "";
}, };
error?: { error?: {
errorMessage: string; errorMessage: string;
} };
} }
const getUserSocialInfo = async (userId: string): Promise<getByIdReturn> => { export const getUserSocialInfo = async (
userId: string
): Promise<getByIdReturn> => {
try { try {
let res: getByIdRes = await usersRoutes.getSocialInfo(userId) let res: getByIdRes = await usersRoutes.getSocialInfo(userId);
if (res.status === "error") { if (res.status === "error") {
return { return {
error: { error: {
errorMessage: res.message, errorMessage: res.message,
} },
}; };
} }
@@ -87,7 +89,7 @@ const getUserSocialInfo = async (userId: string): Promise<getByIdReturn> => {
return { return {
error: { error: {
errorMessage: "Por favor, autentique-se.", errorMessage: "Por favor, autentique-se.",
} },
}; };
} }
}; };
@@ -96,7 +98,7 @@ interface checkIfUserIsDriverReturn {
result?: boolean; result?: boolean;
error?: { error?: {
errorMessage: string; errorMessage: string;
} };
} }
interface checkIfUserIsDriverResponse { interface checkIfUserIsDriverResponse {
@@ -105,18 +107,21 @@ interface checkIfUserIsDriverResponse {
result?: boolean; result?: boolean;
error?: { error?: {
errorMessage: string; errorMessage: string;
} };
} }
const checkIfUserIsDriver = async (id_user: string): Promise<checkIfUserIsDriverReturn> => { export const checkIfUserIsDriver = async (
id_user: string
): Promise<checkIfUserIsDriverReturn> => {
try { try {
let res: checkIfUserIsDriverResponse = await usersRoutes.checkIfUserIsDriver(id_user) let res: checkIfUserIsDriverResponse =
await usersRoutes.checkIfUserIsDriver(id_user);
if (res.status === "error") { if (res.status === "error") {
return { return {
error: { error: {
errorMessage: res.message, errorMessage: res.message,
} },
}; };
} }
@@ -127,9 +132,7 @@ const checkIfUserIsDriver = async (id_user: string): Promise<checkIfUserIsDriver
return { return {
error: { error: {
errorMessage: "Por favor, autentique-se.", errorMessage: "Por favor, autentique-se.",
} },
}; };
} }
}; };
export default { getById, getUserSocialInfo, checkIfUserIsDriver }

View File

@@ -10,3 +10,13 @@ export async function closeToast(setShowToast: React.Dispatch<React.SetStateActi
setShowToast(false) setShowToast(false)
window.history.replaceState({}, document.title) window.history.replaceState({}, document.title)
} }
export function convertNumberToPrice(price: number) {
// Create our number formatter.
var formatter = new Intl.NumberFormat('pt-BR', {
style: 'currency',
currency: 'BRL',
});
return formatter.format(price);
}

19403
yarn.lock

File diff suppressed because it is too large Load Diff