Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 102 additions & 41 deletions ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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",
Expand Down Expand Up @@ -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<string>("");
const [queryMode, setQueryMode] = React.useState<boolean>(false);
const [driverLocationsState, setDriverLocationsState] =
React.useState<NormalizedDriverLocations>({
byId: {},
Expand All @@ -58,6 +76,7 @@ function MyMap() {
byId: {},
allIds: [],
} as NormalizedDriverLocations);
const [searchResults, setSearchResults] = React.useState<SearchResult[]>([]);

const newDriverLocations = getDriverLocationsFromState(
newDriverLocationsState
Expand All @@ -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 (
<Grid container spacing={2}>
<Grid item xs={3}>
<FormGroup>
<FormControlLabel
control={
<Switch
checked={queryMode}
onChange={() => setQueryMode(!queryMode)}
/>
}
label="Mode"
/>
</FormGroup>
</Grid>
<Grid item xs={9}>
{queryMode ? (
<Typography>Click anywhere to rank nearby drivers</Typography>
) : (
<Typography>Click to add new drivers</Typography>
)}
</Grid>
<Grid item xs={3}>
<Item>
<NewDriverList driverLocations={newDriverLocations} />
<DriverList
buildHandleMouseOver={(driverId: string) => () =>
setFocusedDriverId(driverId)}
buildHandleMouseOut={(driverId: string) => () =>
setFocusedDriverId("")}
queryMode={queryMode}
onUpload={() =>
updateDriverLocations(
getDriverLocationsFromState(newDriverLocationsState)
)
}
driverLocations={newDriverLocations}
/>
</Item>
</Grid>
<Grid item xs={9}>
<Item>
<LoadScript googleMapsApiKey={apiKey}>
<GoogleMap
onClick={(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)
);
}}
onClick={queryMode ? getNearbyDrivers : addNewDriver}
options={{
styles: mapStyles,
}}
Expand All @@ -110,13 +174,9 @@ function MyMap() {
<Marker
key={i}
driverLocation={dl}
// TODO move this to Map.onClick, which will AddNewDriver if in Mutation/Ingest Mode, or query/dispatch if in Query/Dispatch Mode
handleClick={(e) =>
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) => (
Expand All @@ -133,22 +193,23 @@ function MyMap() {

function App() {
return (
<Box
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100vh",
backgroundColor: "#282c34",
px: {
xs: "5px",
sm: "10px",
md: "20px",
},
}}
>
<MyMap />
</Box>
<ThemeProvider theme={darkTheme}>
<Item
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100vh",
px: {
xs: "5px",
sm: "10px",
md: "20px",
},
}}
>
<MyMap />
</Item>
</ThemeProvider>
);
}

Expand Down
6 changes: 4 additions & 2 deletions ui/src/Marker/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<Marker
Expand Down
59 changes: 44 additions & 15 deletions ui/src/PointList/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import React, { MouseEventHandler } from "react";
import {
Box,
Button,
Expand Down Expand Up @@ -49,16 +49,21 @@ function stringAvatar(name: string) {

interface ItemProps {
driverLocation: DriverLocation;
buildHandleMouseOver?: (driverId: string) => MouseEventHandler;
buildHandleMouseOut?: (driverId: string) => MouseEventHandler;
}

function Item(props: ItemProps) {
const { driverLocation: dl } = props;
const {
driverLocation: dl,
buildHandleMouseOver,
buildHandleMouseOut,
} = props;
const { driverId } = dl;
return (
<ListItem
onMouseOver={(e) => {
console.log("hovered over list item", e);
}}
onMouseOver={buildHandleMouseOver && buildHandleMouseOver(driverId)}
onMouseOut={buildHandleMouseOut && buildHandleMouseOut(driverId)}
>
<Stack direction="row" spacing={2} alignItems="center">
<Avatar alt={`Driver: ${driverId}`} {...stringAvatar(driverId)} />
Expand All @@ -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 (
<Stack spacing={1} px="20px" py="20px">
<Typography variant="h5" component="div">
New Drivers List
{queryMode ? "Drivers List" : "New Drivers List"}
</Typography>
<Divider />
<List>
{driverLocations.map((p: DriverLocation, i: number) => (
<Item driverLocation={p} key={i} />
<Item
key={i}
driverLocation={p}
buildHandleMouseOver={buildHandleMouseOver}
buildHandleMouseOut={buildHandleMouseOut}
/>
))}
</List>
<Stack justifyContent="center" alignItems="center">
<Button sx={{ maxWidth: "100px" }} variant="outlined">
Submit
</Button>
</Stack>
{!queryMode && (
<Stack justifyContent="center" alignItems="center">
<Button
onClick={handleClick}
sx={{ maxWidth: "100px" }}
variant="outlined"
>
Submit
</Button>
</Stack>
)}
</Stack>
);
}
21 changes: 20 additions & 1 deletion ui/src/request/updateDriverLocations.ts
Original file line number Diff line number Diff line change
@@ -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<void> {
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,
})),
}),
}
);
}
3 changes: 2 additions & 1 deletion ui/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,6 @@ export interface GetNearestDriversResponse {
}

export interface SearchResult {
driverLocation: DriverLocation;
driver: DriverLocation;
distance_meters: number;
}