feat(frontend): maps mvp | close #283

This commit is contained in:
leandrofars 2024-07-05 15:50:50 -03:00
parent 1adbabe654
commit 3cd47b155c
5 changed files with 211 additions and 35 deletions

View File

@ -35,15 +35,15 @@ export const items = [
// </SvgIcon> // </SvgIcon>
// ) // )
// }, // },
// { {
// title: 'Map', title: 'Map',
// path: '/map', path: '/map',
// icon: ( icon: (
// <SvgIcon fontSize="small"> <SvgIcon fontSize="small">
// <MapIcon/> <MapIcon/>
// </SvgIcon> </SvgIcon>
// ), ),
// }, },
{ {
title: 'Credentials', title: 'Credentials',
path: '/credentials', path: '/credentials',

View File

@ -112,6 +112,9 @@ export const SideNav = (props) => {
}} }}
> >
{items.map((item) => { {items.map((item) => {
if (item.title == "Map" && process.env.NEXT_PUBLIC_ENTERPRISE_VERSION != "true"){
return
}
const active = isItemActive(pathname, item.path); const active = isItemActive(pathname, item.path);
return ( return (

View File

@ -10,6 +10,7 @@ import { createTheme } from 'src/theme';
import { createEmotionCache } from 'src/utils/create-emotion-cache'; import { createEmotionCache } from 'src/utils/create-emotion-cache';
import 'simplebar-react/dist/simplebar.min.css'; import 'simplebar-react/dist/simplebar.min.css';
import { WsProvider } from 'src/contexts/socketio-context'; import { WsProvider } from 'src/contexts/socketio-context';
import '../utils/map.css';
const clientSideEmotionCache = createEmotionCache(); const clientSideEmotionCache = createEmotionCache();

View File

@ -1,39 +1,120 @@
import Head from 'next/head'; import Head from 'next/head';
import { GoogleMap, useLoadScript } from "@react-google-maps/api" import { GoogleMap, useLoadScript, Marker, OverlayView } from "@react-google-maps/api"
import { Layout as DashboardLayout } from 'src/layouts/dashboard/layout'; import { Layout as DashboardLayout } from 'src/layouts/dashboard/layout';
import { useEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import mapStyles from '../utils/mapStyles.json'; import mapStyles from '../utils/mapStyles.json';
const getPixelPositionOffset = pixelOffset => (width, height) => ({
x: -(width / 2) + pixelOffset.x,
y: -(height / 2) + pixelOffset.y
});
const Popup = props => {
return (
<OverlayView
position={props.anchorPosition}
mapPaneName={OverlayView.OVERLAY_MOUSE_TARGET}
getPixelPositionOffset={getPixelPositionOffset(props.markerPixelOffset)}
>
<div className="popup-tip-anchor">
<div className="popup-bubble-anchor">
<div className="popup-bubble-content">{props.content}</div>
</div>
</div>
</OverlayView>
);
};
const Page = () => { const Page = () => {
const libraries = useMemo(() => ['places'], []); const libraries = useMemo(() => ['places'], []);
const [mapCenter, setMapCenter] = useState(null); const [mapCenter, setMapCenter] = useState(null);
const [markers, setMarkers] = useState([]);
const [activeMarker, setActiveMarker] = useState(null);
const [activeMarkerdata, setActiveMarkerdata] = useState(null);
const fetchMarkers = async () => {
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
myHeaders.append("Authorization", localStorage.getItem("token"));
var requestOptions = {
method: 'GET',
headers: myHeaders,
redirect: 'follow'
};
let result = await fetch(`${process.env.NEXT_PUBLIC_REST_ENDPOINT || ""}/api/map`, requestOptions)
if (result.status == 200) {
const content = await result.json()
setMarkers(content)
}else if (result.status == 403) {
console.log("num tenx permissão, seu boca de sandália")
return router.push("/403")
}else if (result.status == 401){
console.log("taix nem autenticado, sai fora oh")
return router.push("/auth/login")
} else {
console.log("agora quebrasse ux córno mô quiridu")
const content = await result.json()
throw new Error(content);
}
}
const fetchActiveMarkerData = async (id) => {
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
myHeaders.append("Authorization", localStorage.getItem("token"));
var requestOptions = {
method: 'GET',
headers: myHeaders,
redirect: 'follow'
};
let result = await fetch(`${process.env.NEXT_PUBLIC_REST_ENDPOINT || ""}/api/device?id=`+id, requestOptions)
if (result.status == 200) {
const content = await result.json()
setActiveMarkerdata(content)
}else if (result.status == 403) {
return router.push("/403")
}else if (result.status == 401){
return router.push("/auth/login")
} else {
console.log("no device info found")
const content = await result.json()
}
}
useEffect(()=> { useEffect(()=> {
// Check if geolocation is supported by the browser fetchMarkers();
if ("geolocation" in navigator) { // Check if geolocation is supported by the browser
// Prompt user for permission to access their location if ("geolocation" in navigator) {
navigator.geolocation.getCurrentPosition( // Prompt user for permission to access their location
// Get the user's latitude and longitude coordinates navigator.geolocation.getCurrentPosition(
// Success callback function // Get the user's latitude and longitude coordinates
function(position) { // Success callback function
// Update the map with the user's new location function(position) {
setMapCenter({ // Update the map with the user's new location
lat: position.coords.latitude, setMapCenter({
lng: position.coords.longitude, lat: position.coords.latitude,
}) lng: position.coords.longitude,
}, })
// Error callback function },
function(error) { // Error callback function
// Handle errors, e.g. user denied location sharing permissions function(error) {
console.error("Error getting user location:", error); // Handle errors, e.g. user denied location sharing permissions
} console.error("Error getting user location:", error);
); }
} else { );
// Geolocation is not supported by the browser } else {
console.error("Geolocation is not supported by this browser."); // Geolocation is not supported by the browser
} console.error("Geolocation is not supported by this browser.");
}
},[]) },[])
const mapOptions = useMemo( const mapOptions = useMemo(
@ -59,7 +140,7 @@ const Page = () => {
return <p>Loading...</p>; return <p>Loading...</p>;
} }
return ( mapCenter && return ( mapCenter && markers &&
<> <>
<Head> <Head>
<title> <title>
@ -73,7 +154,51 @@ const Page = () => {
mapContainerStyle={{ width: '100%', height: '100%' }} mapContainerStyle={{ width: '100%', height: '100%' }}
onLoad={() => console.log('Map Component Loaded...')} onLoad={() => console.log('Map Component Loaded...')}
clickableIcons={false} clickableIcons={false}
/> >
{
markers.map((marker, index) => (
<Marker
key={index}
position={{ lat: marker.coordinates.lat, lng: marker.coordinates.lng }}
icon={{
url: marker.img,
scaledSize: new window.google.maps.Size(50, 50),
anchor: new window.google.maps.Point(25, 25),
}}
draggable={false}
clickable={true}
onClick={() => {
setActiveMarkerdata(null);
if (activeMarker?.sn === marker.sn) {
setActiveMarker(null);
return;
}
fetchActiveMarkerData(marker.sn);
setActiveMarker({
sn: marker.sn,
position: { lat: marker.coordinates.lat, lng: marker.coordinates.lng }
});
}}
>
</Marker>
))
}
{activeMarker &&
<Popup
anchorPosition={activeMarker.position}
markerPixelOffset={{ x: 0, y: -32 }}
content={activeMarkerdata ?
<div>
<div>SN: {activeMarker.sn}</div>
<div>
<div>Model: {activeMarkerdata.Model?activeMarkerdata.Model:activeMarkerdata.ProductClass}</div>
<div>Alias: {activeMarkerdata.Alias}</div>
<div>Status: {activeMarkerdata.Status == 2 ? <span style={{color:"green"}}>online</span> : <span style={{color:"green"}}>offline</span>}</div>
</div>
</div>
: <p>no device info found</p>}
/>}
</GoogleMap>
</> </>
)}; )};

View File

@ -0,0 +1,47 @@
/* The location pointed to by the popup tip. */
.popup-tip-anchor {
height: 0;
position: absolute;
/* The max width of the info window. */
width: 200px;
}
/* The bubble is anchored above the tip. */
.popup-bubble-anchor {
position: absolute;
width: 100%;
bottom: /* TIP_HEIGHT= */ 8px;
left: 0;
}
/* Draw the tip. */
.popup-bubble-anchor::after {
content: "";
position: absolute;
top: 0;
left: 0;
/* Center the tip horizontally. */
transform: translate(-50%, 0);
/* The tip is a https://css-tricks.com/snippets/css/css-triangle/ */
width: 0;
height: 0;
/* The tip is 8px high, and 12px wide. */
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-top: /* TIP_HEIGHT= */ 8px solid white;
}
/* The popup bubble itself. */
.popup-bubble-content {
position: absolute;
top: 0;
left: 0;
transform: translate(-50%, -100%);
/* Style the info window. */
background-color: white;
padding: 5px;
border-radius: 5px;
font-family: sans-serif;
font-size: 14px ;
overflow-y: auto;
max-height: 80px;
box-shadow: 0px 2px 10px 1px rgba(0, 0, 0, 0.5);
}