Skip to content

Commit

Permalink
Add display of vehicle speed
Browse files Browse the repository at this point in the history
  • Loading branch information
n1kPLV committed Sep 6, 2023
1 parent 890b407 commit adeb32b
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 48 deletions.
29 changes: 23 additions & 6 deletions Website/src/app/components/dynlist.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
"use client";

import { IMapRefreshConfig, RevalidateError } from "@/utils/types";
import { Vehicle } from "@/utils/api";
import { RevalidateError } from "@/utils/types";
import { FullTrack, Vehicle } from "@/utils/api";
import useSWR from "swr";
import { coordinateFormatter } from "@/utils/helpers";
import Link from "next/link";
import TrackerCharge from "@/app/components/tracker";
import { Dispatch, FunctionComponent } from "react";

const fetcher = async ([url, track_id]: [url: string, track_id: number]) => {
const res = await fetch(`${url}/${track_id}`, { method: "get" });
Expand All @@ -17,14 +18,32 @@ const fetcher = async ([url, track_id]: [url: string, track_id: number]) => {
return res_2;
};

function FocusVehicleLink(props: { v: Vehicle }) {
return <Link href={`/map?focus=${props.v.id}`}>Link</Link>;
}

/**
* A dynamic list of vehicles with their current position.
* @param server_vehicles A pre-fetched list of vehicles to be used until this data is fetched on the client side.
* @param track_id The id of the currently selected track. # TODO: remove redundant variable -> already in track_data
* @param logged_in A boolean indicating if the user is logged in.
* @param track_data The information about the currently selected track.
* @param FocusVehicle Component to focus the specific vehicle.
*/
export default function DynamicList({ server_vehicles, track_id, logged_in, track_data }: IMapRefreshConfig) {
export default function DynamicList({
server_vehicles,
track_id,
logged_in,
track_data,
FocusVehicle = FocusVehicleLink
}: {
server_vehicles: Vehicle[];
track_id: number;
logged_in: boolean;
track_data?: FullTrack;
setLogin: Dispatch<boolean>;
FocusVehicle?: FunctionComponent<{ v: Vehicle }>;
}) {
const { data, error, isLoading } = useSWR(
logged_in && track_id ? ["/webapi/vehicles/list", track_id] : null,
fetcher,
Expand Down Expand Up @@ -89,9 +108,7 @@ export default function DynamicList({ server_vehicles, track_id, logged_in, trac
</div>
</td>
<td className={"px-2 text-center"}>
<Link className="text-blue-600 visited:text-purple-700" href={`/map?focus=${v.id}`}>
Link
</Link>
<FocusVehicle v={v} />
</td>
</tr>
))}
Expand Down
21 changes: 10 additions & 11 deletions Website/src/app/components/login_wrap.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"use client";
import { IMapRefreshConfig } from "@/utils/types";
import { useState } from "react";
import { Dispatch, FunctionComponent, useState } from "react";
import { LoginDialog } from "@/app/components/login";
import { SelectionDialog } from "@/app/components/track_selection";

Expand All @@ -9,19 +8,19 @@ import { SelectionDialog } from "@/app/components/track_selection";
* @param logged_in initial login state
* @param track_selected track selection state
* @param map_conf parameters for the construction of the child
* @param child Function contructing the wrapped React Component.
* @param Child The wrapped React Component.
*/
const LoginWrapper = ({
function LoginWrapper<T extends object>({
logged_in,
track_selected,
map_conf,
child
childConf,
child: Child
}: {
logged_in: boolean;
track_selected: boolean;
map_conf: IMapRefreshConfig;
child: (conf: IMapRefreshConfig) => JSX.Element;
}) => {
childConf: T;
child: FunctionComponent<T & { logged_in: boolean; setLogin: Dispatch<boolean> }>;
}) {
const [loginState, setLogin] = useState(logged_in);

// console.log('track selected', track_selected, map_conf.track_id)
Expand All @@ -39,9 +38,9 @@ const LoginWrapper = ({
</SelectionDialog>
)
)}
{child({ ...map_conf, logged_in: loginState, setLogin: setLogin })}
<Child {...childConf} logged_in={loginState} setLogin={setLogin} />
</>
);
};
}

export default LoginWrapper;
18 changes: 15 additions & 3 deletions Website/src/app/components/map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import "leaflet-rotatedmarker";
import "leaflet/dist/leaflet.css";
import { useEffect, useMemo, useRef, useState } from "react";
import { IMapConfig } from "@/utils/types";
import { coordinateFormatter } from "@/utils/helpers";
import { coordinateFormatter, speedFormatter } from "@/utils/helpers";
import assert from "assert";
import { createPortal } from "react-dom";
import RotatingVehicleIcon from "@/utils/rotatingIcon";
Expand Down Expand Up @@ -169,7 +169,7 @@ function Map({
// We can then use a React portal to place content in there.
const popupElement = document.createElement("div");
popupElement.className = "w-96 flex p-1.5 flex-row flex-wrap";
m.bindPopup(popupElement, {className: 'w-auto', maxWidth: undefined});
m.bindPopup(popupElement, { className: "w-auto", maxWidth: undefined });
setPopupContainer(popupElement);
// unset the focussed element on popup closing.
m.on("popupclose", () => {
Expand Down Expand Up @@ -235,7 +235,13 @@ function Map({
Vehicle &quot;{vehicleInFocus?.name}&quot;
</h2>
<div className={"basis-1/3"}>Tracker-Ladezustand:</div>
<div className={"basis-2/3"}>{vehicleInFocus ? vehicleInFocus.trackerIds.map(trackerId => <TrackerCharge key={trackerId} trackerId={trackerId} />) : "unbekannt"}</div>
<div className={"basis-2/3"}>
{vehicleInFocus
? vehicleInFocus.trackerIds.map(trackerId => (
<TrackerCharge key={trackerId} trackerId={trackerId} />
))
: "unbekannt"}
</div>
<div className={"basis-1/3"}>Position:</div>
<div className={"basis-2/3"}>
{vehicleInFocus?.pos ? (
Expand All @@ -247,6 +253,12 @@ function Map({
"unbekannt"
)}
</div>
<div className={"basis-1/3"}>Geschwindigkeit:</div>
<div className={"basis-2/3"}>
{vehicleInFocus?.speed != undefined && vehicleInFocus.speed !== -1
? speedFormatter.format(vehicleInFocus.speed)
: "unbekannt"}
</div>
</>
) : (
<div />
Expand Down
38 changes: 13 additions & 25 deletions Website/src/app/list/page.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,33 @@
import { cookies } from "next/headers";
import { getTrackData, getAllVehiclesOnTrack, getAllPOIsOnTrack, getAllPOITypes } from "@/utils/data";
import { getAllVehiclesOnTrack, getTrackData } from "@/utils/data";
import LoginWrapper from "@/app/components/login_wrap";
import { FullTrack, PointOfInterest, POIType, Vehicle } from "@/utils/api";
import { FullTrack, Vehicle } from "@/utils/api";
import DynamicList from "@/app/components/dynlist";

export default async function Home() {
const token = cookies().get("token")?.value;
const track_id = parseInt(cookies().get("track_id")?.value ?? "", 10);
const track_selected = !isNaN(track_id);
const [track_data, server_vehicles, points_of_interest, poi_types]: [
FullTrack | undefined,
Vehicle[],
PointOfInterest[],
POIType[]
] = !(token && track_selected)
? [undefined, [] as Vehicle[], [] as PointOfInterest[], [] as POIType[]]
: await Promise.all([
getTrackData(token, track_id),
getAllVehiclesOnTrack(token, track_id),
getAllPOIsOnTrack(token, track_id),
getAllPOITypes(token)
]).catch(e => {
const [track_data, server_vehicles]: [FullTrack | undefined, Vehicle[]] = !(token && track_selected)
? [undefined, [] as Vehicle[]]
: await Promise.all([getTrackData(token, track_id), getAllVehiclesOnTrack(token, track_id)]).catch(e => {
console.error("Error fetching Map Data from the Backend:", e);
return [undefined, [], [], []];
return [undefined, []];
});

const listConf = {
server_vehicles,
track_data,
track_id
};

return (
<main className="mx-auto w-full max-w-4xl grow">
<div className={"bg-white dark:bg-slate-800 dark:text-white p-4 rounded"}>
<LoginWrapper
logged_in={token !== undefined}
track_selected={track_selected}
map_conf={{
position: { lat: 54.2333, lng: 10.6024 },
zoom_level: 11,
server_vehicles,
track_data,
track_id,
points_of_interest,
poi_types
}}
childConf={listConf}
child={DynamicList}
/>
</div>
Expand Down
3 changes: 1 addition & 2 deletions Website/src/app/map/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { FullTrack, PointOfInterest, POIType, Vehicle } from "@/utils/api";
import { nanToUndefined } from "@/utils/helpers";

export default async function MapPage({ searchParams }: { searchParams: { focus?: string; success?: string } }) {

// get the login token and the ID of the selected track
const token = cookies().get("token")?.value;
const track_id = parseInt(cookies().get("track_id")?.value ?? "", 10);
Expand Down Expand Up @@ -49,7 +48,7 @@ export default async function MapPage({ searchParams }: { searchParams: { focus?
<LoginWrapper
logged_in={token !== undefined}
track_selected={track_selected}
map_conf={{
childConf={{
position: { lat: 54.2333, lng: 10.6024 },
zoom_level: 11.5,
server_vehicles,
Expand Down
1 change: 1 addition & 0 deletions Website/src/utils/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export type Vehicle = UpdateVehicle & {
pos?: Position; // undefined if position is unknown.
percentagePosition?: number; // A position mapped onto percentage 0-100) e.g. 0% Malente; 100% Lütjenburg
heading?: number; // between 0 and 360
speed?: number; // in km/h
};

/**
Expand Down
9 changes: 8 additions & 1 deletion Website/src/utils/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const async_sleep: (time: number) => Promise<null> = time =>

export const batteryLevelFormatter = new Intl.NumberFormat("de-DE", {
notation: "standard",
style: "unit",
style: "percent",
unit: "percent",
maximumFractionDigits: 1
});
Expand All @@ -20,6 +20,13 @@ export const coordinateFormatter = new Intl.NumberFormat("de-DE", {
maximumFractionDigits: 4
});

export const speedFormatter = new Intl.NumberFormat("de-DE", {
notation: "standard",
style: "unit",
unit: "kilometer-per-hour",
maximumFractionDigits: 1
});

export function nanToUndefined(x: number): number | undefined {
if (Number.isNaN(x)) return;
return x;
Expand Down

0 comments on commit adeb32b

Please sign in to comment.