Merge pull request #16 from Hzin/bugfix/hot-reload

Bugfix/hot reload
This commit is contained in:
CloudAlb
2022-09-13 22:09:11 -03:00
committed by GitHub
10 changed files with 9339 additions and 9871 deletions

View File

@@ -84,7 +84,11 @@
"@types/lodash.isequal": "^4.5.6",
"autoprefixer": "^9.8.8",
"postcss": "^7.0.39",
"react-error-overlay": "6.0.9",
"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

@@ -33,7 +33,7 @@ import MeusItinerarios from "./pages/MeusItinerarios/MeusItinerarios";
import Buscas from "./pages/Buscas";
import BuscarItinerario from "./pages/BuscarItinerario";
import BuscarPassageiro from "./pages/BuscarPassageiro/BuscarPassageiro";
import Transportes from "./pages/Transportes/Transportes";
import ListaItinerarios from "./pages/ListaItinerarios";
/* Core CSS required for Ionic components to work properly */
import "@ionic/react/css/core.css";
@@ -93,7 +93,7 @@ const routes = (
<Route exact path="/buscas" component={Buscas}></Route>
<Route exact path="/buscar/itinerario" component={BuscarItinerario}></Route>
<Route exact path="/buscar/passageiro" component={BuscarPassageiro}></Route>
<Route exact path="/transportes" component={Transportes}></Route>
<Route exact path="/buscar/itinerario/lista" component={ListaItinerarios}></Route>
<Route exact path="/">
<Redirect to="/home" />

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 {
pageName: string;
@@ -10,7 +16,12 @@ export const PageHeader = (props: ComponentProps) => (
<IonToolbar>
<IonTitle>{props.pageName}</IonTitle>
<IonButtons slot="start">
<IonBackButton text="" defaultHref={ props.backButtonPageUrl ? props.backButtonPageUrl : undefined } />
<IonBackButton
text=""
defaultHref={
props.backButtonPageUrl ? props.backButtonPageUrl : undefined
}
/>
</IonButtons>
</IonToolbar>
</IonHeader>

View File

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

View File

@@ -1,31 +1,19 @@
import {
IonBackButton,
IonButton,
IonButtons,
IonCard,
IonCardContent,
IonCardHeader,
IonCardSubtitle,
IonCardTitle,
IonContent,
IonHeader,
IonIcon,
IonItem,
IonItemDivider,
IonPage,
IonRow,
IonTitle,
IonToast,
IonToolbar,
} from "@ionic/react";
import {
arrowForwardOutline,
cashOutline,
chevronForwardOutline,
locateOutline,
locationOutline,
personOutline,
starOutline,
timeOutline,
} from "ionicons/icons";
import "./BuscarItinerario.css";
@@ -39,7 +27,6 @@ import GooglePlacesAutocomplete, {
geocodeByAddress,
getLatLng,
} from "react-google-places-autocomplete";
import { Itinerary } from "../models/itinerary.model";
import { PageHeader } from "../components/PageHeader";
import { closeToast } from "../services/utils";
@@ -56,47 +43,9 @@ const BuscarItinerario: React.FC = () => {
const [coordinatesFrom, setCoordinatesFrom] = useState<any>("");
const [addressTo, setAddressTo] = useState<any>("");
const [coordinatesTo, setCoordinatesTo] = useState<any>("");
const [showModalEnd, setShowModalEnd] = useState(false);
const [addressResults, setAddressResults] = useState<any[]>([]);
const [inputActive, setInputActive] = useState("");
const [recentSearches, setRecentSearches] = useState<any[]>([]);
const [itinerariesList, setItinerariesList] = useState<Itinerary[]>();
// const optionsAddress = async (inputValue: any) => {
// let results = await autoCompleteAddress(inputValue)
// .then((res) => {
// return res.map((item: any) => {
// return {
// value:
// item.geometry.coordinates[0] + "," + item.geometry.coordinates[1],
// label: item.properties.formatted,
// };
// });
// })
// .catch((err) => {
// console.log("Erro ao buscar endereço:", err);
// });
// setAddressResults(results);
// };
// function setInputActiveOpenModal(input: string) {
// setInputActive(input);
// setShowModalEnd(true);
// }
// function setAddress(div: any) {
// if (inputActive === "from") {
// setAddressFrom(div.target.attributes[2].value);
// setCoordinatesFrom(div.target.attributes[1].value);
// } else {
// setAddressTo(div.target.attributes[2].value);
// setCoordinatesTo(div.target.attributes[1].value);
// }
// setShowModalEnd(false);
// }
useEffect(() => {
if (addressFrom.label && addressFrom.label.length > 0) {
geocodeByAddress(addressFrom.label)
@@ -118,10 +67,12 @@ const BuscarItinerario: React.FC = () => {
return;
}
const maxRecentSearchesLength = 0
const maxRecentSearchesLength = 0;
if (recentSearches.length >= maxRecentSearchesLength) {
setRecentSearches(recentSearches.slice(recentSearches.length - maxRecentSearchesLength));
setRecentSearches(
recentSearches.slice(recentSearches.length - maxRecentSearchesLength)
);
}
setRecentSearches((arr) => [
@@ -134,10 +85,12 @@ const BuscarItinerario: React.FC = () => {
]);
await itinerariesService
.searchItineraries({
coordinatesFrom,
coordinatesTo,
})
// TODO, desfazer
// .searchItineraries({
// coordinatesFrom,
// coordinatesTo,
// })
.getAllItineraries()
.then((response) => {
// if (response.status === "error") {
// setToastColor("danger");
@@ -147,7 +100,18 @@ const BuscarItinerario: React.FC = () => {
// return;
// }
setItinerariesList(response);
history.push({
pathname: "/buscar/itinerario/lista",
state: {
coordinatesFrom,
coordinatesTo,
addressFrom,
addressTo,
itineraries: response,
},
});
// setItinerariesList(response);
})
.catch((err) => {
setToastColor("danger");
@@ -225,7 +189,8 @@ const BuscarItinerario: React.FC = () => {
return (
<>
<div>
<IonRow key={index}
<IonRow
key={index}
class="latest-searches"
onClick={() => {
fillSearchBars(search.addressFrom, search.addressTo);
@@ -258,102 +223,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
color={toastColor}

View File

@@ -70,9 +70,14 @@
display: flex;
justify-content: center;
align-items: center;
margin: 1rem 0 5.5rem 0;
/* margin: 1rem 0 5.5rem 0; */
margin-top: 20px;
}
.msg-not-found{
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

@@ -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,6 +1,6 @@
import instance from '../services/api/api';
export async function autoCompleteAddress(address:string) {
export async function autoCompleteAddress(address: string) {
const response = await instance.get(`https://api.geoapify.com/v1/geocode/autocomplete?text=${address}&apiKey=ee574aacff6f440a84378bbbf7e2f20d`);
return response.data.features;
@@ -10,3 +10,13 @@ export async function closeToast(setShowToast: React.Dispatch<React.SetStateActi
setShowToast(false)
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);
}

18500
yarn.lock

File diff suppressed because it is too large Load Diff