feat: add sidebar, landing page, orphanages map, and API service with styles
This commit is contained in:
22
web/src/components/Sidebar.tsx
Normal file
22
web/src/components/Sidebar.tsx
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { FiArrowLeft } from 'react-icons/fi';
|
||||||
|
import { useNavigate } from 'react-router-dom'; // Changed from useHistory
|
||||||
|
import mapMarkerImg from '../images/map_marker.svg';
|
||||||
|
|
||||||
|
import '../styles/components/sidebar.css';
|
||||||
|
|
||||||
|
export default function Sidebar () {
|
||||||
|
const navigate = useNavigate(); // Changed from useHistory
|
||||||
|
|
||||||
|
return (
|
||||||
|
<aside className="app-sidebar">
|
||||||
|
<img src={mapMarkerImg} alt="Happy" />
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<button type="button" onClick={() => navigate(-1)}> {/* Changed from goBack */}
|
||||||
|
<FiArrowLeft size={24} color="#FFF" />
|
||||||
|
</button>
|
||||||
|
</footer>
|
||||||
|
</aside>
|
||||||
|
)
|
||||||
|
}
|
||||||
32
web/src/pages/landing-page.tsx
Normal file
32
web/src/pages/landing-page.tsx
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { FiArrowRight } from 'react-icons/fi';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
import '../styles/landing-page.css';
|
||||||
|
|
||||||
|
import logoimg from '../images/logo.svg';
|
||||||
|
|
||||||
|
function LandingPage() {
|
||||||
|
return (
|
||||||
|
<div id="landing-page">
|
||||||
|
<div className="content-wrapper">
|
||||||
|
<img src={logoimg} alt="Happy" />
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<h1>Leve felicidade para o mundo!</h1>
|
||||||
|
<p>Visite orfanatos e mude o dia de muitas crianças.</p>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<div className="location">
|
||||||
|
<strong>Santos</strong>
|
||||||
|
<span>São Paulo</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Link to="/app" className="enter-app">
|
||||||
|
<FiArrowRight size="26" color="rgba(0, 0, 0, 0.6)" />
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LandingPage;
|
||||||
112
web/src/pages/orphanages-page.tsx
Normal file
112
web/src/pages/orphanages-page.tsx
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import { FiPlus, FiArrowRight } from 'react-icons/fi';
|
||||||
|
import { GoogleMap, useJsApiLoader, Marker, InfoWindow } from '@react-google-maps/api';
|
||||||
|
|
||||||
|
import '../styles/orphanages.css';
|
||||||
|
import MapMarker from '../images/map_marker.svg';
|
||||||
|
import api from '../services/api';
|
||||||
|
|
||||||
|
interface Orphanage {
|
||||||
|
id: number;
|
||||||
|
latitude: number;
|
||||||
|
longitude: number;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const containerStyle = {
|
||||||
|
width: '100%',
|
||||||
|
height: '100%'
|
||||||
|
};
|
||||||
|
|
||||||
|
const center = {
|
||||||
|
lat: -23.9550537,
|
||||||
|
lng: -46.3204442
|
||||||
|
};
|
||||||
|
|
||||||
|
function OrphanagesMap() {
|
||||||
|
const { isLoaded } = useJsApiLoader({
|
||||||
|
id: 'google-map-script',
|
||||||
|
googleMapsApiKey: process.env.REACT_APP_MAPS_API_KEY!
|
||||||
|
});
|
||||||
|
|
||||||
|
const [orphanages, setOrphanages] = useState<Orphanage[]>([]);
|
||||||
|
const [selectedOrphanage, setSelectedOrphanage] = useState<Orphanage | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
api.get('orphanages').then(response => {
|
||||||
|
setOrphanages(response.data);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (!isLoaded) {
|
||||||
|
return <div>Loading Map...</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div id="page-map">
|
||||||
|
<aside>
|
||||||
|
<header>
|
||||||
|
<img src={MapMarker} alt="Happy"/>
|
||||||
|
<h1>Escolha um orfanato no mapa</h1>
|
||||||
|
<p>Muitas crianças estão esperando a sua visita! :)</p>
|
||||||
|
</header>
|
||||||
|
<footer>
|
||||||
|
<strong>Santos</strong>
|
||||||
|
<span>São Paulo</span>
|
||||||
|
</footer>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<GoogleMap
|
||||||
|
key={process.env.REACT_APP_MAPS_API_KEY!}
|
||||||
|
mapContainerStyle={containerStyle}
|
||||||
|
center={center}
|
||||||
|
zoom={14}
|
||||||
|
options={{
|
||||||
|
disableDefaultUI: true,
|
||||||
|
zoomControl: true,
|
||||||
|
zoomControlOptions: {
|
||||||
|
position: window.google.maps.ControlPosition.TOP_LEFT
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
onClick={() => setSelectedOrphanage(null)}
|
||||||
|
>
|
||||||
|
{orphanages.map(orphanage => (
|
||||||
|
<Marker
|
||||||
|
key={orphanage.id}
|
||||||
|
position={{ lat: orphanage.latitude, lng: orphanage.longitude }}
|
||||||
|
icon={{
|
||||||
|
url: MapMarker,
|
||||||
|
scaledSize: new window.google.maps.Size(58, 68)
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedOrphanage(orphanage);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{selectedOrphanage && (
|
||||||
|
<InfoWindow
|
||||||
|
position={{ lat: selectedOrphanage.latitude, lng: selectedOrphanage.longitude }}
|
||||||
|
onCloseClick={() => {
|
||||||
|
setSelectedOrphanage(null);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="map-popup">
|
||||||
|
{selectedOrphanage.name}
|
||||||
|
<Link to={`/orphanage/${selectedOrphanage.id}`}>
|
||||||
|
<FiArrowRight size={20} color="#FFF"></FiArrowRight>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</InfoWindow>
|
||||||
|
)}
|
||||||
|
</GoogleMap>
|
||||||
|
|
||||||
|
<Link to="/orphanages/create" className="create-orphanage">
|
||||||
|
<FiPlus size="32" color="#FFF"></FiPlus>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OrphanagesMap;
|
||||||
7
web/src/services/api.ts
Normal file
7
web/src/services/api.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const api = axios.create({
|
||||||
|
baseURL: process.env.BACKEND_URL || 'http://localhost:3101',
|
||||||
|
});
|
||||||
|
|
||||||
|
export default api;
|
||||||
39
web/src/styles/components/sidebar.css
Normal file
39
web/src/styles/components/sidebar.css
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
aside.app-sidebar {
|
||||||
|
position: fixed;
|
||||||
|
height: 100%;
|
||||||
|
padding: 32px 24px;
|
||||||
|
background: linear-gradient(329.54deg, #15B6D6 0%, #15D6D6 100%);
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
aside.app-sidebar img {
|
||||||
|
width: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
aside.app-sidebar footer a,
|
||||||
|
aside.app-sidebar footer button {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
|
||||||
|
border: 0;
|
||||||
|
|
||||||
|
background: #12AFCB;
|
||||||
|
border-radius: 16px;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
aside.app-sidebar footer a:hover,
|
||||||
|
aside.app-sidebar footer button:hover {
|
||||||
|
background: #17D6EB;
|
||||||
|
}
|
||||||
109
web/src/styles/orphanages.css
Normal file
109
web/src/styles/orphanages.css
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
#page-map {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
#page-map aside {
|
||||||
|
width: 440px;
|
||||||
|
background: linear-gradient(329.54deg, #29b6d1 0%, #00c7c7 100%);
|
||||||
|
|
||||||
|
padding: 80px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
#page-map aside h1 {
|
||||||
|
font-weight: 800;
|
||||||
|
font-size: 40px;
|
||||||
|
line-height: 42px;
|
||||||
|
margin-top: 64px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#page-map aside p {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 28px;
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#page-map aside footer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
#page-map aside footer {
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#page-map aside footer strong {
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
#page-map .create-orphanage {
|
||||||
|
z-index: 10;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 40px;
|
||||||
|
right: 40px;
|
||||||
|
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
background-color: #15C3D6;
|
||||||
|
border-radius: 20px;
|
||||||
|
box-shadow: 0px 4px 10px rgba(23, 142, 166, 0.4); /* Shadow added */
|
||||||
|
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#page-map .create-orphanage:hover {
|
||||||
|
background-color: #17d6eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- NEW STYLING FOR GOOGLE MAPS INFO WINDOW --- */
|
||||||
|
.map-popup {
|
||||||
|
color: #0089a5;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-popup a {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
background: #15c3d6;
|
||||||
|
box-shadow: 17.2868px 27.6589px 41.4884px rgba(23, 142, 166, 0.16);
|
||||||
|
border-radius: 12px;
|
||||||
|
margin-left: 12px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This targets the default close button on the Google InfoWindow */
|
||||||
|
.gm-ui-hover-effect {
|
||||||
|
top: 4px !important;
|
||||||
|
right: 4px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This targets the default white background and arrow of the Google InfoWindow */
|
||||||
|
.gm-style-iw-c {
|
||||||
|
padding: 12px !important;
|
||||||
|
border-radius: 20px !important;
|
||||||
|
}
|
||||||
|
.gm-style-iw-d {
|
||||||
|
overflow: hidden !important;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user