feat: add sidebar, landing page, orphanages map, and API service with styles

This commit is contained in:
2025-06-11 18:32:20 -03:00
parent 31050675dd
commit 2682bfb9c1
6 changed files with 321 additions and 0 deletions

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

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

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

@@ -0,0 +1,7 @@
import axios from 'axios';
const api = axios.create({
baseURL: process.env.BACKEND_URL || 'http://localhost:3101',
});
export default api;

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

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