Make map nodes selectable and stylish
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
12c43e9a00
commit
ed420d5ff1
@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { MapContainer, TileLayer, Marker, CircleMarker, Popup, Polyline, LayerGroup, GeoJSON, } from 'react-leaflet';
|
import { MapContainer, TileLayer, Marker, CircleMarker, Popup, Polyline, LayerGroup, GeoJSON, } from 'react-leaflet';
|
||||||
import 'leaflet/dist/leaflet.css';
|
import 'leaflet/dist/leaflet.css';
|
||||||
import L, { LatLngBounds } from 'leaflet';
|
import L, { LatLngBounds } from 'leaflet';
|
||||||
@ -7,30 +7,33 @@ import { useQuery, useMutation, useQueryClient, queryOptions } from "@tanstack/r
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
//Todo: Move types to own file
|
//Todo: Move types to own file
|
||||||
type User = {
|
interface User {
|
||||||
name: string;
|
name: string;
|
||||||
id: string;
|
id: string;
|
||||||
email: string;
|
email: string;
|
||||||
phoneNumber: string;
|
phoneNumber: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Product = {
|
interface Node {
|
||||||
|
name: string;
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
}
|
||||||
|
|
||||||
|
interface Product extends Node {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
weight?: number;
|
weight?: number;
|
||||||
picture: string;
|
picture: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
// type Location = {
|
// interface Location = {
|
||||||
// latitude: number;
|
// latitude: number;
|
||||||
// longitude: number;
|
// longitude: number;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
type Maker = {
|
interface Maker extends Node {
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
email: string;
|
email: string;
|
||||||
phoneNumber?: string;
|
phoneNumber?: string;
|
||||||
location: [number, number];
|
location: [number, number];
|
||||||
@ -39,9 +42,7 @@ type Maker = {
|
|||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type Retailer = {
|
interface Retailer extends Node {
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
email: string;
|
email: string;
|
||||||
phoneNumber?: string;
|
phoneNumber?: string;
|
||||||
location: [number, number];
|
location: [number, number];
|
||||||
@ -53,7 +54,7 @@ type Retailer = {
|
|||||||
const DISPATCH_STATUS = ['requested', 'accepted', 'archived'] as const;
|
const DISPATCH_STATUS = ['requested', 'accepted', 'archived'] as const;
|
||||||
type DispatchStatus = typeof DISPATCH_STATUS[number];
|
type DispatchStatus = typeof DISPATCH_STATUS[number];
|
||||||
|
|
||||||
type Dispatch = {
|
interface Dispatch {
|
||||||
id: string;
|
id: string;
|
||||||
dispatchesCode?: string; //Human readable id
|
dispatchesCode?: string; //Human readable id
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
@ -107,7 +108,7 @@ const getRetailers = async () => {
|
|||||||
console.log("Fetching url:", url)
|
console.log("Fetching url:", url)
|
||||||
const response = await axios.get(url);
|
const response = await axios.get(url);
|
||||||
|
|
||||||
const retailers:Retailer[] = response.data.docs;
|
const retailers: Retailer[] = response.data.docs;
|
||||||
console.log(`Fetch result from ${url}`, retailers)
|
console.log(`Fetch result from ${url}`, retailers)
|
||||||
return retailers;
|
return retailers;
|
||||||
|
|
||||||
@ -126,7 +127,7 @@ const getDispatches = async () => {
|
|||||||
console.log("Fetching url:", url)
|
console.log("Fetching url:", url)
|
||||||
const response = await axios.get(url);
|
const response = await axios.get(url);
|
||||||
|
|
||||||
const dispatches:Dispatch[] = response.data.docs;
|
const dispatches: Dispatch[] = response.data.docs;
|
||||||
|
|
||||||
console.log(`Fetch result from ${url}`, dispatches)
|
console.log(`Fetch result from ${url}`, dispatches)
|
||||||
return dispatches;
|
return dispatches;
|
||||||
@ -143,7 +144,7 @@ const useGetDispatches = () => {
|
|||||||
|
|
||||||
//Payload longitude and latitude are mislabeled in payload (lol)
|
//Payload longitude and latitude are mislabeled in payload (lol)
|
||||||
const locationSwitcharoo = (location: number[]) => {
|
const locationSwitcharoo = (location: number[]) => {
|
||||||
if(location.length === 2) {
|
if (location.length === 2) {
|
||||||
const correctedLocation = [location[1], location[0]]
|
const correctedLocation = [location[1], location[0]]
|
||||||
return correctedLocation;
|
return correctedLocation;
|
||||||
}
|
}
|
||||||
@ -164,25 +165,40 @@ const dashColors: Record<DispatchStatus, string> = {
|
|||||||
archived: '#000',
|
archived: '#000',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const dashColorSelected: string = '#f87171' //same as tw red 400
|
||||||
|
|
||||||
const dashOpacities: Record<DispatchStatus, number> = {
|
const dashOpacities: Record<DispatchStatus, number> = {
|
||||||
requested: 0.7,
|
requested: 0.7,
|
||||||
accepted: 0.7,
|
accepted: 0.7,
|
||||||
archived: 0.5
|
archived: 0.5
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const KiosMap = () => {
|
export const KiosMap = () => {
|
||||||
|
|
||||||
const { data: makers, isLoading: isLoadingMakers } = useGetMakers();
|
const { data: makers, isLoading: isLoadingMakers } = useGetMakers();
|
||||||
const { data: retailers, isLoading: isLoadingRetailers } = useGetRetailers();
|
const { data: retailers, isLoading: isLoadingRetailers } = useGetRetailers();
|
||||||
const { data: dispatches, isLoading: isLoadingDispatches } = useGetDispatches();
|
const { data: dispatches, isLoading: isLoadingDispatches } = useGetDispatches();
|
||||||
|
|
||||||
|
const [selectedNodeId, setSelectedNodeId] = useState<string>('')
|
||||||
|
|
||||||
|
const handleSelectNode = (nodeId: string) => {
|
||||||
|
setSelectedNodeId(nodeId)
|
||||||
|
console.log("set id:", nodeId)
|
||||||
|
}
|
||||||
|
|
||||||
const blackDotIcon = L.divIcon({
|
const blackDotIcon = L.divIcon({
|
||||||
className: 'bg-gray-950 rounded-full',
|
className: 'bg-gray-950 rounded-full',
|
||||||
iconSize: [20, 20],
|
iconSize: [20, 20],
|
||||||
iconAnchor: [10, 10]
|
iconAnchor: [10, 10]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const selectedDotIcon = L.divIcon({
|
||||||
|
className: 'bg-red-400 rounded-full ring-offset-2 ring-4 ring-red-400 ring-dashed',
|
||||||
|
iconSize: [20, 20],
|
||||||
|
iconAnchor: [10, 10]
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const bounds = new LatLngBounds(
|
const bounds = new LatLngBounds(
|
||||||
[-90, -180], // Southwest corner
|
[-90, -180], // Southwest corner
|
||||||
@ -191,7 +207,9 @@ export const KiosMap = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-full flex justify-center align-middle'>
|
<div className='w-full flex justify-center align-middle'>
|
||||||
|
<div className=''>
|
||||||
|
<div></div>
|
||||||
|
</div>
|
||||||
<MapContainer
|
<MapContainer
|
||||||
id="map"
|
id="map"
|
||||||
center={[-6.1815, 106.8228]}
|
center={[-6.1815, 106.8228]}
|
||||||
@ -209,11 +227,14 @@ export const KiosMap = () => {
|
|||||||
<LayerGroup>
|
<LayerGroup>
|
||||||
{makers.map((maker: any, index: number) => (
|
{makers.map((maker: any, index: number) => (
|
||||||
<Marker
|
<Marker
|
||||||
key={index}
|
eventHandlers={{
|
||||||
|
click: () => handleSelectNode(maker.id)
|
||||||
|
}}
|
||||||
|
key={maker.id}
|
||||||
position={[locationSwitcharoo(maker.location)[0], locationSwitcharoo(maker.location)[1]]}
|
position={[locationSwitcharoo(maker.location)[0], locationSwitcharoo(maker.location)[1]]}
|
||||||
icon={blackDotIcon}
|
icon={selectedNodeId === maker.id ? selectedDotIcon : blackDotIcon}
|
||||||
>
|
>
|
||||||
<Popup>{maker.name}</Popup>
|
{/* <Popup>{maker.name}</Popup> */}
|
||||||
</Marker>
|
</Marker>
|
||||||
))}
|
))}
|
||||||
</LayerGroup>
|
</LayerGroup>
|
||||||
@ -223,11 +244,14 @@ export const KiosMap = () => {
|
|||||||
<LayerGroup>
|
<LayerGroup>
|
||||||
{retailers.map((retailer: any, index: number) => (
|
{retailers.map((retailer: any, index: number) => (
|
||||||
<Marker
|
<Marker
|
||||||
key={index}
|
eventHandlers={{
|
||||||
|
click: () => handleSelectNode(retailer.id)
|
||||||
|
}}
|
||||||
|
key={retailer.id}
|
||||||
position={[locationSwitcharoo(retailer.location)[0], locationSwitcharoo(retailer.location)[1]]}
|
position={[locationSwitcharoo(retailer.location)[0], locationSwitcharoo(retailer.location)[1]]}
|
||||||
icon={blackDotIcon}
|
icon={selectedNodeId === retailer.id ? selectedDotIcon : blackDotIcon}
|
||||||
>
|
>
|
||||||
<Popup>{retailer.name}</Popup>
|
{/* <Popup>{retailer.name}</Popup> */}
|
||||||
</Marker>
|
</Marker>
|
||||||
))}
|
))}
|
||||||
</LayerGroup>
|
</LayerGroup>
|
||||||
@ -237,7 +261,7 @@ export const KiosMap = () => {
|
|||||||
<LayerGroup>
|
<LayerGroup>
|
||||||
{dispatches.map((dispatch: any, index: number) => {
|
{dispatches.map((dispatch: any, index: number) => {
|
||||||
|
|
||||||
if(dispatch.maker && dispatch.retailer) {
|
if (dispatch.maker && dispatch.retailer) {
|
||||||
|
|
||||||
const start = locationSwitcharoo(dispatch.maker.location);
|
const start = locationSwitcharoo(dispatch.maker.location);
|
||||||
const end = locationSwitcharoo(dispatch.retailer.location);
|
const end = locationSwitcharoo(dispatch.retailer.location);
|
||||||
@ -251,18 +275,20 @@ export const KiosMap = () => {
|
|||||||
//status type should already be inferred when list of dispatches is created, weird that is is required
|
//status type should already be inferred when list of dispatches is created, weird that is is required
|
||||||
const status: DispatchStatus = dispatch.status;
|
const status: DispatchStatus = dispatch.status;
|
||||||
|
|
||||||
const dashArray:string = dashArrays[status]
|
const dashArray: string = dashArrays[status]
|
||||||
const dashColor:string = dashColors[status]
|
const dashColor: string = dashColors[status]
|
||||||
const dashOpacity:number = dashOpacities[status]
|
const dashOpacity: number = dashOpacities[status]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={index}>
|
|
||||||
<Polyline
|
<Polyline
|
||||||
|
eventHandlers={{
|
||||||
|
click: () => handleSelectNode(dispatch.id)
|
||||||
|
}}
|
||||||
|
key={dispatch.id}
|
||||||
positions={[[start[0], start[1]], [end[0], end[1]]]}
|
positions={[[start[0], start[1]], [end[0], end[1]]]}
|
||||||
color={dashColor}
|
pathOptions={{color: selectedNodeId === dispatch.id ? dashColorSelected : dashColor}}
|
||||||
opacity={dashOpacity}
|
opacity={dashOpacity}
|
||||||
dashArray={dashArray} />
|
dashArray={dashArray} />
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
|
@ -63,7 +63,7 @@ export interface Dispatch {
|
|||||||
}
|
}
|
||||||
export interface Product {
|
export interface Product {
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
name: string;
|
||||||
picture: string | Media;
|
picture: string | Media;
|
||||||
weight?: number;
|
weight?: number;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
|
@ -3,14 +3,14 @@ import { CollectionConfig } from 'payload/types';
|
|||||||
const Products: CollectionConfig = {
|
const Products: CollectionConfig = {
|
||||||
slug: 'products',
|
slug: 'products',
|
||||||
admin: {
|
admin: {
|
||||||
useAsTitle: 'title',
|
useAsTitle: 'name',
|
||||||
},
|
},
|
||||||
access: {
|
access: {
|
||||||
read: () => true,
|
read: () => true,
|
||||||
},
|
},
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: 'title',
|
name: 'name',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user