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