From cbd864960b539ab9ac210257bf0b633035fbce63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=A4ckelmann?= <6890706+n1kPLV@users.noreply.github.com> Date: Tue, 19 Sep 2023 21:17:43 +0200 Subject: [PATCH 1/5] Restructured Header --- Website/src/app/components/base_layout.tsx | 4 +-- .../{header.tsx => layout/currentUser.tsx} | 19 ++++++-------- .../app/components/{ => layout}/footer.tsx | 0 Website/src/app/components/layout/header.tsx | 25 +++++++++++++++++++ Website/src/app/components/login.tsx | 2 +- .../src/app/components/track_selection.tsx | 2 +- 6 files changed, 37 insertions(+), 15 deletions(-) rename Website/src/app/components/{header.tsx => layout/currentUser.tsx} (59%) rename Website/src/app/components/{ => layout}/footer.tsx (100%) create mode 100644 Website/src/app/components/layout/header.tsx diff --git a/Website/src/app/components/base_layout.tsx b/Website/src/app/components/base_layout.tsx index 39572048..6a0e0eef 100644 --- a/Website/src/app/components/base_layout.tsx +++ b/Website/src/app/components/base_layout.tsx @@ -1,6 +1,6 @@ import "./globals.css"; -import Header from "@/app/components/header"; -import Footer from "@/app/components/footer"; +import Header from "@/app/components/layout/header"; +import Footer from "@/app/components/layout/footer"; import React from "react"; /** diff --git a/Website/src/app/components/header.tsx b/Website/src/app/components/layout/currentUser.tsx similarity index 59% rename from Website/src/app/components/header.tsx rename to Website/src/app/components/layout/currentUser.tsx index ea06bc59..dbd771db 100644 --- a/Website/src/app/components/header.tsx +++ b/Website/src/app/components/layout/currentUser.tsx @@ -1,33 +1,30 @@ "use client"; -import Link from "next/link"; import { useContext } from "react"; import { UsernameContext } from "@/app/components/username-provider"; +import Link from "next/link"; /** - * The header for the web page + * A component showing the name of the currently logged-in user, with a logout link, + * or a login link if the user is not logged-in. */ -export default function Header() { +export function CurrentUser({ className }: { className?: string }) { const username = useContext(UsernameContext); return ( -
-
- RailTrail Admin interface -
-
+ <> {username ? ( -
+
Hello {username} –{" "} Logout
) : ( -
+
Login
)} -
+ ); } diff --git a/Website/src/app/components/footer.tsx b/Website/src/app/components/layout/footer.tsx similarity index 100% rename from Website/src/app/components/footer.tsx rename to Website/src/app/components/layout/footer.tsx diff --git a/Website/src/app/components/layout/header.tsx b/Website/src/app/components/layout/header.tsx new file mode 100644 index 00000000..babaec65 --- /dev/null +++ b/Website/src/app/components/layout/header.tsx @@ -0,0 +1,25 @@ +import Link from "next/link"; +import { CurrentUser } from "@/app/components/layout/currentUser"; + +/** + * The header for the web page + */ +export default function Header() { + return ( +
+
+ RailTrail Verwaltung +
+ + {/* Force a line break for small devices */} +
+ +
Foo
+
Bar
+
Baz
+
+ ); +} diff --git a/Website/src/app/components/login.tsx b/Website/src/app/components/login.tsx index 9a43c495..b927e357 100644 --- a/Website/src/app/components/login.tsx +++ b/Website/src/app/components/login.tsx @@ -3,7 +3,7 @@ import { useRouter } from "next/navigation"; import { FormEventHandler, PropsWithChildren, Suspense, useEffect, useRef, useState } from "react"; -import Footer from "@/app/components/footer"; +import Footer from "@/app/components/layout/footer"; import { ErrorMessage } from "@/app/management/components/errorMessage"; /** diff --git a/Website/src/app/components/track_selection.tsx b/Website/src/app/components/track_selection.tsx index cf16dc25..dc9b60cd 100644 --- a/Website/src/app/components/track_selection.tsx +++ b/Website/src/app/components/track_selection.tsx @@ -2,7 +2,7 @@ import { Dispatch, FormEventHandler, PropsWithChildren, useEffect, useRef, useState } from "react"; -import Footer from "@/app/components/footer"; +import Footer from "@/app/components/layout/footer"; import useSWR from "swr"; import { setCookie } from "cookies-next"; import { inter } from "@/utils/common"; From 09d78519fd6114bab93b714580dc26ab2c042b40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=A4ckelmann?= <6890706+n1kPLV@users.noreply.github.com> Date: Tue, 19 Sep 2023 23:36:54 +0200 Subject: [PATCH 2/5] Added smart-ish links to the header --- Website/src/app/components/dynlist.tsx | 14 ++++--- Website/src/app/components/dynmap.tsx | 5 +++ .../src/app/components/dynmap_with_list.tsx | 2 +- Website/src/app/components/form.tsx | 16 ++++---- Website/src/app/components/layout/header.tsx | 17 +++++--- .../components/layout/smart_navigation.tsx | 39 +++++++++++++++++++ .../src/app/components/selectTrackButton.tsx | 14 +++++++ .../src/app/components/track_selection.tsx | 32 +++++++++++---- Website/src/app/components/tracker.tsx | 8 ++-- Website/src/app/list/page.tsx | 2 +- Website/src/app/management/layout.tsx | 20 +++++++++- Website/src/utils/helpers.ts | 10 +++-- 12 files changed, 141 insertions(+), 38 deletions(-) create mode 100644 Website/src/app/components/layout/smart_navigation.tsx create mode 100644 Website/src/app/components/selectTrackButton.tsx diff --git a/Website/src/app/components/dynlist.tsx b/Website/src/app/components/dynlist.tsx index c41a2ec0..d7d21139 100644 --- a/Website/src/app/components/dynlist.tsx +++ b/Website/src/app/components/dynlist.tsx @@ -9,6 +9,7 @@ import TrackerCharge from "@/app/components/tracker"; import { FunctionComponent } from "react"; import { getFetcher } from "@/utils/fetcher"; import { useRouter } from "next/navigation"; +import { SelectTrackButton } from "@/app/components/selectTrackButton"; /** * A component to focus a vehicle. A link to the map view with the respective search parameter @@ -77,7 +78,7 @@ export function VehicleList({ )} -
+
{v.trackerIds.map(trackerId => ( ))} @@ -127,7 +128,7 @@ export default function DynamicList({ const sorted_vehicles = vehicles?.sort((a, b) => a.id - b.id); // obtain the NextJS router - const router = useRouter() + const router = useRouter(); if (logged_in && error) { if (error instanceof UnauthorizedError || (error instanceof RevalidateError && error.statusCode === 401)) { @@ -139,9 +140,12 @@ export default function DynamicList({ return ( <> -

- Fahrzeuge der Strecke {track_data?.start} - {track_data?.end} -

+
+

+ Fahrzeuge der Strecke {track_data?.start} - {track_data?.end} +

+ +
); diff --git a/Website/src/app/components/dynmap.tsx b/Website/src/app/components/dynmap.tsx index b403f645..3386b58f 100644 --- a/Website/src/app/components/dynmap.tsx +++ b/Website/src/app/components/dynmap.tsx @@ -6,6 +6,7 @@ import useSWR from "swr"; import { useRouter } from "next/navigation"; import { useState } from "react"; import { getFetcher } from "@/utils/fetcher"; +import { SelectTrackButton } from "@/app/components/selectTrackButton"; // This complicated thing with `dynamic` is necessary to disable server side rendering // for the actual map, which does not work with leaflet. @@ -76,6 +77,10 @@ export default function DynamicMap({ setFocus }} /> + {/* This will stack over the map, if all map layers have a z-index < 1100 (which should be the default) */} +
+ +
); } diff --git a/Website/src/app/components/dynmap_with_list.tsx b/Website/src/app/components/dynmap_with_list.tsx index 07118955..8ce6c848 100644 --- a/Website/src/app/components/dynmap_with_list.tsx +++ b/Website/src/app/components/dynmap_with_list.tsx @@ -84,7 +84,7 @@ export default function DynamicMapList({ }} />
-
+
-
- {children} -
- - ) +export function FormWrapper({ children }: { children: React.ReactNode }) { + return ( +
+
{children}
+
+ ); } // TODO: create a component for a form in a dialog to replace/refactor -// the LoginDialog and SelectionDialog components. \ No newline at end of file +// the LoginDialog and SelectionDialog components. diff --git a/Website/src/app/components/layout/header.tsx b/Website/src/app/components/layout/header.tsx index babaec65..3b55a5ef 100644 --- a/Website/src/app/components/layout/header.tsx +++ b/Website/src/app/components/layout/header.tsx @@ -1,5 +1,6 @@ import Link from "next/link"; import { CurrentUser } from "@/app/components/layout/currentUser"; +import SmartNavigation from "@/app/components/layout/smart_navigation"; /** * The header for the web page @@ -8,18 +9,22 @@ export default function Header() { return (
RailTrail Verwaltung
- + {/* Force a line break for small devices */} -
+
-
Foo
-
Bar
-
Baz
+ Karte + Liste + + + Datenverwaltung
); } diff --git a/Website/src/app/components/layout/smart_navigation.tsx b/Website/src/app/components/layout/smart_navigation.tsx new file mode 100644 index 00000000..7b287e57 --- /dev/null +++ b/Website/src/app/components/layout/smart_navigation.tsx @@ -0,0 +1,39 @@ +"use client"; + +import Link, { LinkProps } from "next/link"; +import { usePathname } from "next/navigation"; +import { PropsWithChildren } from "react"; + +/** + * A navigation link that has a different style when the user is on the page it links to, or on a sub-page + * @param href The URL to navigate to + * @param className CSS classes for the enclosing div + * @param activeClassName Additional CSS classes for the enclosing div, when active + * @param linkClassName The CSS classes for the anchor tag + * @param children The contents in the anchor tag + * @param props Other options applicable to + * @constructor + */ +export default function SmartNavigation({ + href, + className = "px-2 border-2 border-transparent", + linkClassName, + children, + ...props +}: PropsWithChildren) { + // get the path of the currently open page + const currentPath = usePathname(); + + // and determine if we are currently on that path + const active = (currentPath === href || currentPath?.startsWith(href + "/")) ?? false; + + const activeClassName = "bg-neutral-500/20 !border-gray-500 rounded"; + + return ( +
+ + {children} + +
+ ); +} diff --git a/Website/src/app/components/selectTrackButton.tsx b/Website/src/app/components/selectTrackButton.tsx new file mode 100644 index 00000000..0e2e5b48 --- /dev/null +++ b/Website/src/app/components/selectTrackButton.tsx @@ -0,0 +1,14 @@ +import Link from "next/link"; + +/** + * A link that somewhat resembles a button to select a different track. + */ +export function SelectTrackButton() { + return ( + + Andere Strecke wählen + + ); +} diff --git a/Website/src/app/components/track_selection.tsx b/Website/src/app/components/track_selection.tsx index dc9b60cd..12e8c6a4 100644 --- a/Website/src/app/components/track_selection.tsx +++ b/Website/src/app/components/track_selection.tsx @@ -1,10 +1,8 @@ -"use client"; - import { Dispatch, FormEventHandler, PropsWithChildren, useEffect, useRef, useState } from "react"; import Footer from "@/app/components/layout/footer"; import useSWR from "swr"; -import { setCookie } from "cookies-next"; +import { getCookie, setCookie } from "cookies-next"; import { inter } from "@/utils/common"; import { getFetcher } from "@/utils/fetcher"; import { useRouter } from "next/navigation"; @@ -24,6 +22,7 @@ export default function Selection({ const { data, error, isLoading } = useSWR("/webapi/tracks/list", getFetcher<"/webapi/tracks/list">); // get the next page router const router = useRouter(); + const selectedTrack = getCookie("track_id")?.toString(); const selectTrack: FormEventHandler = e => { e.preventDefault(); @@ -41,7 +40,7 @@ export default function Selection({ }; return ( -
+ {isLoading ? (
@@ -52,14 +51,18 @@ export default function Selection({ ) : completed ? (
-
Wird gepeichert...
+
Wird gespeichert...
) : ( <> - {data?.map(({ id, start, end }) => (