Skip to content

Commit

Permalink
Additional documentation (#166)
Browse files Browse the repository at this point in the history
* Added some jsdoc comments

* Added even more JSDoc comments describing every function in this website
  • Loading branch information
n1kPLV authored Sep 14, 2023
1 parent 70df23e commit 4d59997
Show file tree
Hide file tree
Showing 57 changed files with 477 additions and 263 deletions.
2 changes: 1 addition & 1 deletion Website/src/app/components/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ This folder contains various React components, that should have general purpose
(i.e. useful for more than one route.)

As these are co-located in the `app`-directory, these files are server-components
by default. But as many of them use client side side-effects, they are explicitly
by default. But as many of them use client-side side effects, they are explicitly
declared to be client-components using the `"use client"`-directive.
See: <https://nextjs.org/docs/getting-started/react-essentials>
14 changes: 12 additions & 2 deletions Website/src/app/components/dynlist.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,20 @@ import TrackerCharge from "@/app/components/tracker";
import { FunctionComponent } from "react";
import { getFetcher } from "@/utils/fetcher";

function FocusVehicleLink(props: { v: Vehicle }) {
return <Link href={`/map?focus=${props.v.id}`}>Link</Link>;
/**
* A component to focus a vehicle. A link to the map view with the respective search parameter
* @param v The vehicle to focus
*/
function FocusVehicleLink({ v }: { v: Vehicle }) {
return <Link href={`/map?focus=${v.id}`}>Link</Link>;
}

/**
* A table of vehicles
* @param sorted_vehicles A sorted list of the vehicles
* @param FocusVehicle Component to focus a specific vehicle
* @param compact Flag for "compact" mode (removes some columns)
*/
export function VehicleList({
sorted_vehicles,
FocusVehicle,
Expand Down
12 changes: 12 additions & 0 deletions Website/src/app/components/dynmap_with_list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@ const _internal_DynamicMap = dynamic(() => import("@/app/components/map"), {
ssr: false
});

/**
* The dynamic part of the "advanced side-by-side" UI
* @param initial_focus The id of the initially focussed vehicle
* @param track_data The data of the selected track
* @param logged_in Whether the user is currently logged in
* @param initial_position The initial map position
* @param server_vehicles A server-fetched list of vehicles
* @param track_id The id of the selected track
* @param initial_zoom_level The initial zoom level of the map
* @param points_of_interest A list of points of interest on the track
* @param poi_types A list of point of interest types
*/
export default function DynamicMapList({
initial_focus,
track_data,
Expand Down
3 changes: 3 additions & 0 deletions Website/src/app/components/form_map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ function InternalPositionSelector({
}).addTo(mapRef.current);
}

/**
* Add a draggable marker for selecting a position
*/
function addMarker() {
assert(mapRef.current != undefined, "Error: Map not ready!");

Expand Down
5 changes: 5 additions & 0 deletions Website/src/app/components/map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ import { PointOfInterest, POIType, POITypeIconValues } from "@/utils/api";
import { POIIconImg } from "@/utils/common";
import TrackerCharge from "@/app/components/tracker";

/**
* Constructs the content of the popup for a POI, without React
* @param poi The POI to construct the popup for
* @param poi_type The type of that POI
*/
function poiPopupFactory(poi: PointOfInterest, poi_type?: POIType): HTMLDivElement {
const container = document.createElement("div");

Expand Down
3 changes: 3 additions & 0 deletions Website/src/app/components/reloadButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
import { useRouter } from "next/navigation";
import { PropsWithChildren } from "react";

/**
* A button that, when clicked, requests Next to re-render the page it is on.
*/
export function ReloadButton({ className, children }: PropsWithChildren<{ className?: string }>) {
const router = useRouter();

Expand Down
3 changes: 3 additions & 0 deletions Website/src/app/components/spinner.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
/**
* A spinning loading indicator. Will pulse instead if the user prefers reduced motion
*/
export function Spinner({ className }: { className?: string }) {
return (
<svg className={className} viewBox={"0 0 48 48"}>
Expand Down
2 changes: 1 addition & 1 deletion Website/src/app/components/track_selection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export default function Selection({
// set the relevant cookie
setCookie("track_id", data.get("track"));

// change the react state
// change the React state
setCompleted(true);

// and reload
Expand Down
5 changes: 5 additions & 0 deletions Website/src/app/components/tracker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ import { getFetcher, TrackerIdRoute } from "@/utils/fetcher";
import useSWR from "swr";
import { batteryLevelFormatter } from "@/utils/helpers";

/**
* Component displaying the charge of a tracker with a given
* @param trackerId
* @constructor
*/
export default function TrackerCharge({ trackerId }: { trackerId: string }) {
const safeTrackerId = encodeURIComponent(trackerId);
const { data: tracker_data } = useSWR(`/webapi/tracker/read/${safeTrackerId}`, getFetcher<TrackerIdRoute>);
Expand Down
2 changes: 1 addition & 1 deletion Website/src/app/components/username-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { createContext, PropsWithChildren } from "react";

/**
* A react context hodling the username of the currently logged in user
* A React context holding the username of the currently logged in user
*/
export const UsernameContext = createContext(undefined as undefined | string);

Expand Down
4 changes: 4 additions & 0 deletions Website/src/app/list/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import LoginWrapper from "@/app/components/login_wrap";
import { FullTrack, Vehicle } from "@/utils/api";
import DynamicList from "@/app/components/dynlist";

/**
* A page containing only the vehicle list
* @constructor
*/
export default async function Home() {
const token = cookies().get("token")?.value;
const track_id = parseInt(cookies().get("track_id")?.value ?? "", 10);
Expand Down
3 changes: 3 additions & 0 deletions Website/src/app/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import { FormWrapper } from "@/app/components/form";
import Link from "next/link";
import { useState } from "react";

/**
* The stand-alone login page
*/
export default function LoginPage() {
const [login, setLogin] = useState(false);

Expand Down
152 changes: 90 additions & 62 deletions Website/src/app/management/add_track/page.tsx
Original file line number Diff line number Diff line change
@@ -1,70 +1,98 @@
'use client'
"use client";

import {FormEvent, useRef, useState} from "react";
import {UpdateTrack} from "@/utils/api";
import { FormEvent, useRef, useState } from "react";
import { UpdateTrack } from "@/utils/api";

/**
* The form used to add a track to the system
*/
export default function Home() {
const formRef = useRef(null as (null | HTMLFormElement))
const [success, setSuccess] = useState(false);
const [error, setError] = useState(undefined as string | undefined)
const formRef = useRef(null as null | HTMLFormElement);
const [success, setSuccess] = useState(false);
const [error, setError] = useState(undefined as string | undefined);

async function submit(e: FormEvent) {
e.preventDefault()
const form = formRef.current;
if (!form) throw new Error('Form missing');
const formData = new FormData(form);
const trackFile = formData.get('track') as File
const path = JSON.parse(await trackFile.text())
/**
* Submit the data from the form to the backend.
* @param e The form submit event.
*/
async function submit(e: FormEvent) {
e.preventDefault();
const form = formRef.current;
if (!form) throw new Error("Form missing");
const formData = new FormData(form);
const trackFile = formData.get("track") as File;
const path = JSON.parse(await trackFile.text());

const uploadRequest: UpdateTrack = {
path,
start: formData.get('trackStart') as string,
end: formData.get('trackEnd') as string,
}
const uploadRequest: UpdateTrack = {
path,
start: formData.get("trackStart") as string,
end: formData.get("trackEnd") as string
};

try {
const result = await fetch('/webapi/tracks/new', {
body: JSON.stringify(uploadRequest),
headers: {"Content-Type": "application/json"},
method: 'POST'
},)
try {
const result = await fetch("/webapi/tracks/new", {
body: JSON.stringify(uploadRequest),
headers: { "Content-Type": "application/json" },
method: "POST"
});

if (result.ok) {
setSuccess(true);
setError(undefined)
} else {
if (result.status == 401)
setError('Authorisierungsfehler: Sind Sie angemeldet?')
if (result.status >= 500 && result.status < 600)
setError(`Serverfehler ${result.status} ${result.statusText}`)
}
} catch (e) {
setError(`Fehler: Konnte Anfrage nicht senden: ${e}`)
}
if (result.ok) {
setSuccess(true);
setError(undefined);
} else {
if (result.status == 401) setError("Authorisierungsfehler: Sind Sie angemeldet?");
if (result.status >= 500 && result.status < 600)
setError(`Serverfehler ${result.status} ${result.statusText}`);
}
} catch (e) {
setError(`Fehler: Konnte Anfrage nicht senden: ${e}`);
}
}

}

return (
<>
{success ? <p className="bg-green-300 border-green-600 text-black rounded p-2 text-center">Track erfolgreich
hinzugefügt</p> :
<form action={"#"} onSubmit={submit} ref={formRef}
className={'grid grid-cols-8 gap-y-2 mx-1.5 items-center'}>
<label htmlFor={'trackStart'} className={'col-span-3'}>Name des Startpunktes</label><input
id={'trackStart'} name={'trackStart'}
type={"text"} className="border border-gray-500 rounded col-span-5 dark:bg-slate-700"/>
<label htmlFor={'trackEnd'} className={'col-span-3'}>Name des Endpunktes</label><input
id={'trackEnd'} name={'trackEnd'} type={"text"}
className="border border-gray-500 rounded col-span-5 dark:bg-slate-700"/>
<label htmlFor={'track'} className={'col-span-3'}>Positionsdaten (GeoJSON)</label><input id='track'
name={'track'}
type={"file"}
className={'col-span-5'}/>
{error && <div
className='col-span-8 bg-red-300 border-red-600 text-black rounded p-2 text-center'>{error}</div>}
<button type={"submit"} className="col-span-8 rounded-full bg-gray-700 text-white">Absenden</button>
</form>
}
</>
)
}
return (
<>
{success ? (
<p className="bg-green-300 border-green-600 text-black rounded p-2 text-center">
Track erfolgreich hinzugefügt
</p>
) : (
<form
action={"#"}
onSubmit={submit}
ref={formRef}
className={"grid grid-cols-8 gap-y-2 mx-1.5 items-center"}>
<label htmlFor={"trackStart"} className={"col-span-3"}>
Name des Startpunktes
</label>
<input
id={"trackStart"}
name={"trackStart"}
type={"text"}
className="border border-gray-500 rounded col-span-5 dark:bg-slate-700"
/>
<label htmlFor={"trackEnd"} className={"col-span-3"}>
Name des Endpunktes
</label>
<input
id={"trackEnd"}
name={"trackEnd"}
type={"text"}
className="border border-gray-500 rounded col-span-5 dark:bg-slate-700"
/>
<label htmlFor={"track"} className={"col-span-3"}>
Positionsdaten (GeoJSON)
</label>
<input id="track" name={"track"} type={"file"} className={"col-span-5"} />
{error && (
<div className="col-span-8 bg-red-300 border-red-600 text-black rounded p-2 text-center">
{error}
</div>
)}
<button type={"submit"} className="col-span-8 rounded-full bg-gray-700 text-white">
Absenden
</button>
</form>
)}
</>
);
}
3 changes: 3 additions & 0 deletions Website/src/app/management/components/iconSelection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ export default function IconSelection({
[currentIcon, iconOptions, defaultIcon]
);

/**
* Function handling the selection of a different icon
*/
function changeFunction(newValue: SingleValue<Option<POITypeIcon | "">>) {
if (newValue && newValue.value !== "") {
setIcon(newValue.value);
Expand Down
7 changes: 7 additions & 0 deletions Website/src/app/management/components/managementForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ import { SubmitButtons } from "@/app/management/components/submitButtons";
import { Response } from "next/dist/compiled/@edge-runtime/primitives";
import { type HTTP_METHOD } from "next/dist/server/web/http";

/**
* Adjust the form state based on the response from the backend
* @param result The response received from the backend
* @param mutate_fkt A function that indicates that data has changed, when called
* @param setSuccess A function to set the success state of the form
* @param setError A function to set the error state of the form
*/
async function handleResponse(
result: Response,
mutate_fkt: () => Promise<void>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import StyledSelect from "@/app/management/components/styledSelect";
* @param setValue Function to set the id of the selected object
* @param setModified Function to set the form state to modified
* @param objects Objects which can be selected
* @param mappingFunction Function mapping an object to a selectable option (i.e. a Option<ValueType>)
* @param mappingFunction Function mapping an object to a selectable option (i.e. an Option<ValueType>)
* @param width The grid-width of the selection thingy.
*/
export default function ReferencedObjectSelect<ValueType, ObjectType>({
Expand Down
7 changes: 7 additions & 0 deletions Website/src/app/management/components/submitButtons.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { FormEventHandler } from "react";

/**
* A pair of buttons for the management forms. Contains a form submit button with text either being "Hinzufügen" or "Ändern",
* and a delete button.
* @param creating Flag to indicate whether the form is in create mode
* @param onDelete Function to call when the delete button is clicked.
* @constructor
*/
export function SubmitButtons({ creating, onDelete }: { creating: boolean; onDelete: FormEventHandler }) {
return (
<>
Expand Down
5 changes: 5 additions & 0 deletions Website/src/app/management/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { FormWrapper } from "@/app/components/form";
import React from "react";

/**
* The
* @param children
* @constructor
*/
export default function Layout({ children }: { children: React.ReactNode }) {
return <FormWrapper>{children}</FormWrapper>;
}
Loading

0 comments on commit 4d59997

Please sign in to comment.