diff --git a/ui/src/App.tsx b/ui/src/App.tsx index f31418e..a3314e7 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -3,9 +3,17 @@ import "./App.css"; import { GoogleMap, LoadScript } from "@react-google-maps/api"; import Marker from "./Marker"; import mapStyles from "./mapStyles.json"; -import { Box, Grid } from "@mui/material"; +import { ThemeProvider, createTheme } from "@mui/material/styles"; +import { + Box, + FormControlLabel, + FormGroup, + Grid, + Switch, + Typography, +} from "@mui/material"; import { styled } from "@mui/material/styles"; -import NewDriverList from "./PointList"; +import DriverList from "./PointList"; import Paper from "@mui/material/Paper"; import { useEffectOnce } from "usehooks-ts"; import { @@ -15,10 +23,18 @@ import { getDriverLocationsFromState, LatLng, NormalizedDriverLocations, + SearchResult, } from "./types"; import { faker } from "@faker-js/faker"; import listDrivers from "./request/listDrivers"; import getNearestDrivers from "./request/getNearestDrivers"; +import updateDriverLocations from "./request/updateDriverLocations"; + +const darkTheme = createTheme({ + palette: { + mode: "dark", + }, +}); const Item = styled(Paper)(({ theme }) => ({ backgroundColor: theme.palette.mode === "dark" ? "#1A2027" : "#fff", @@ -48,6 +64,8 @@ function newDriverName(): string { function MyMap() { const apiKey = process.env.REACT_APP_GOOGLE_MAPS_API_KEY as string; + const [focusedDriverId, setFocusedDriverId] = React.useState(""); + const [queryMode, setQueryMode] = React.useState(false); const [driverLocationsState, setDriverLocationsState] = React.useState({ byId: {}, @@ -58,6 +76,7 @@ function MyMap() { byId: {}, allIds: [], } as NormalizedDriverLocations); + const [searchResults, setSearchResults] = React.useState([]); const newDriverLocations = getDriverLocationsFromState( newDriverLocationsState @@ -73,32 +92,77 @@ function MyMap() { getDriverLocations().catch(console.error); }); + const getNearbyDrivers = async (e: google.maps.MapMouseEvent) => { + const lat = e.latLng?.lat() ?? 0; + const lng = e.latLng?.lng() ?? 0; + + const res = await getNearestDrivers({ + latitude: lat, + longitude: lng, + } as LatLng); + setSearchResults(res?.results ?? []); + }; + + const addNewDriver = (e: google.maps.MapMouseEvent) => { + const lat = e.latLng?.lat() ?? 0; + const lng = e.latLng?.lng() ?? 0; + console.log(`clicked (${lat}, ${lng})`); + const dl = { + driverId: newDriverName(), + currentLocation: { + latitude: lat, + longitude: lng, + }, + } as DriverLocation; + setNewDriverLocationsState( + addDriverLocationToState(newDriverLocationsState, dl) + ); + }; + return ( + + + setQueryMode(!queryMode)} + /> + } + label="Mode" + /> + + + + {queryMode ? ( + Click anywhere to rank nearby drivers + ) : ( + Click to add new drivers + )} + - + () => + setFocusedDriverId(driverId)} + buildHandleMouseOut={(driverId: string) => () => + setFocusedDriverId("")} + queryMode={queryMode} + onUpload={() => + updateDriverLocations( + getDriverLocationsFromState(newDriverLocationsState) + ) + } + driverLocations={newDriverLocations} + /> { - const lat = e.latLng?.lat() ?? 0; - const lng = e.latLng?.lng() ?? 0; - console.log(`clicked (${lat}, ${lng})`); - const dl = { - driverId: newDriverName(), - currentLocation: { - latitude: lat, - longitude: lng, - }, - } as DriverLocation; - setNewDriverLocationsState( - addDriverLocationToState(newDriverLocationsState, dl) - ); - }} + onClick={queryMode ? getNearbyDrivers : addNewDriver} options={{ styles: mapStyles, }} @@ -110,13 +174,9 @@ function MyMap() { - getNearestDrivers({ - latitude: e.latLng?.lat() ?? 0, - longitude: e.latLng?.lng() ?? 0, - }) - } + isNear={searchResults + .map((sr) => sr.driver.driverId) + .includes(dl.driverId)} /> ))} {newDriverLocations.map((dl: DriverLocation, i: number) => ( @@ -133,22 +193,23 @@ function MyMap() { function App() { return ( - - - + + + + + ); } diff --git a/ui/src/Marker/index.tsx b/ui/src/Marker/index.tsx index a70e8f6..83013b4 100644 --- a/ui/src/Marker/index.tsx +++ b/ui/src/Marker/index.tsx @@ -16,11 +16,13 @@ interface MyMarkerProps { handleMouseOver?: (e: google.maps.MapMouseEvent) => void; handleClick?: (e: google.maps.MapMouseEvent) => void; cached?: boolean; + isNear?: boolean; } export default function MyMarker(props: MyMarkerProps) { - const { cached, driverLocation, handleClick, handleMouseOver } = props; - const color = cached ? "orange" : "#FFD700"; + const { cached, isNear, driverLocation, handleClick, handleMouseOver } = + props; + const color = isNear ? "green" : cached ? "orange" : "#FFD700"; const p = driverLocation.currentLocation; return ( MouseEventHandler; + buildHandleMouseOut?: (driverId: string) => MouseEventHandler; } function Item(props: ItemProps) { - const { driverLocation: dl } = props; + const { + driverLocation: dl, + buildHandleMouseOver, + buildHandleMouseOut, + } = props; const { driverId } = dl; return ( { - console.log("hovered over list item", e); - }} + onMouseOver={buildHandleMouseOver && buildHandleMouseOver(driverId)} + onMouseOut={buildHandleMouseOut && buildHandleMouseOut(driverId)} > @@ -68,28 +73,52 @@ function Item(props: ItemProps) { ); } -interface NewDriverListProps { +interface DriverListProps { driverLocations: DriverLocation[]; + onUpload?: MouseEventHandler; + /** + * Whether the list is showing nearby ranked drivers (Query Mode) or cached drivers that will be created (Mutate Mode) + */ + queryMode: boolean; + buildHandleMouseOver?: (driverId: string) => MouseEventHandler; + buildHandleMouseOut?: (driverId: string) => MouseEventHandler; } -export default function NewDriverList(props: NewDriverListProps) { - const { driverLocations } = props; +export default function DriverList(props: DriverListProps) { + const { + driverLocations, + onUpload: handleClick, + queryMode, + buildHandleMouseOver, + buildHandleMouseOut, + } = props; return ( - New Drivers List + {queryMode ? "Drivers List" : "New Drivers List"} {driverLocations.map((p: DriverLocation, i: number) => ( - + ))} - - - + {!queryMode && ( + + + + )} ); } diff --git a/ui/src/request/updateDriverLocations.ts b/ui/src/request/updateDriverLocations.ts index da53d3b..b6d0111 100644 --- a/ui/src/request/updateDriverLocations.ts +++ b/ui/src/request/updateDriverLocations.ts @@ -1 +1,20 @@ -export default async function () {} +import { BASE_INIT, BASE_URL } from "./base"; +import { DriverLocation } from "../types"; + +export default async function ( + driverLocations: DriverLocation[] +): Promise { + const now = new Date().toISOString(); + await fetch( + `${BASE_URL}/coop.drivers.dispatch.v1beta1.DispatchService/UpdateDriverLocations`, + { + ...BASE_INIT, + body: JSON.stringify({ + locations: driverLocations.map((dl) => ({ + ...dl, + mostRecentHeartbeat: now, + })), + }), + } + ); +} diff --git a/ui/src/types.ts b/ui/src/types.ts index bbd918e..9dd2aa3 100644 --- a/ui/src/types.ts +++ b/ui/src/types.ts @@ -52,5 +52,6 @@ export interface GetNearestDriversResponse { } export interface SearchResult { - driverLocation: DriverLocation; + driver: DriverLocation; + distance_meters: number; }