Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added a list of vehicles #43

Merged
merged 6 commits into from
Aug 9, 2023
Merged
Show file tree
Hide file tree
Changes from 4 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
17 changes: 17 additions & 0 deletions Website/src/app/components/layout_base.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import '../../components/globals.css'
import Header from "@/app/components/header";
import Footer from "@/app/components/footer";

export default function Layout({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A quick look through this shows me that there are practically no comments in the code and no documentation for the public API. That makes it a lot harder to understand and also review code such as this. Please comment your code.

children,
}: {
children: React.ReactNode
}) {
return (
<div className='h-full min-h-screen flex flex-initial flex-col'>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To organize the styling between the different files all the stylings should be structured into .css files and only referenced by their class here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WON'T FIX: this sort of styling is only used for this component. There is absolutely nothing gained by defining a custom CSS class page_container that magically applies the same style. Additionally, I would then have to look at two different files to figure out why something is styled like it is. (Also it would defy the use of tailwind for this page)

I would argue, that it also helps with maintainability, as there are no 'orphaned' CSS styles hanging around in a CSS file somewhere, that might still be in use

However, (not in this case, but maybe for some other thing) I should move some duplicated layout things into a component or a layout.tsx file.

<Header/>
{children}
<Footer/>
</div>
)
}
20 changes: 14 additions & 6 deletions Website/src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import RootLayout from "@/components/layout"
import Layout from "@/app/components/layout_base"
import {inter, meta_info} from "@/lib/common";

export const metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
}
export const metadata = meta_info;

export default RootLayout;
export default function RootLayout({children,}: { children: React.ReactNode }) {
return (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naming here seems inconsistent: the layout_base file contains theLayout and the layout file the RootLayout.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Files named layout.ts(x) (in the app-directory) have special meaning in next.js as these define a component that "wrapps" all page components in subdirectories. As I however need to use the same layout in both the app-, as well as the pages-directory. I factored the relevant parts into a component.

However I agree that naming things is hard

<html lang="en">
<body className={inter.className}>
<Layout>
{children}
</Layout>
</body>
</html>
)
}


40 changes: 40 additions & 0 deletions Website/src/app/list/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@

import {cookies} from 'next/headers';
import {getInitData, getVehicleData} from '@/lib/data';
import LoginWrapper from "@/components/login_wrap";
import {InitResponse, Vehicle} from "@/lib/api.website";
import DynamicList from "@/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);
let server_vehicles: Vehicle[];
let init_data: InitResponse | undefined;
try {
init_data = (token && track_selected) ? await getInitData(token, track_id) : undefined;
server_vehicles = (token && track_selected) ? await getVehicleData(token, track_id) : [];
} catch (e) {
console.error('Catched e:', e);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error messages should be a bit more informative about the context in which they occurred.

init_data = undefined;
server_vehicles = []
}

console.log("server vehicles", server_vehicles)
return (
<main className="container mx-auto max-w-4xl grow">
<div className={'bg-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,
init_data,
track_id
}
} child={DynamicList}/>
</div>
</main>
)
}
4 changes: 2 additions & 2 deletions Website/src/app/login/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import Login from "@/components/login";


export default function Home(x: any) {
export default function Home() {

return (
// <div className='h-full min-h-screen'>
<main className="container mx-auto max-w-2xl grow">
<div className={'bg-white p-4 rounded'}>
<div className={'bg-white dark:bg-slate-800 dark:text-white p-4 rounded'}>
<Login/>
</div>
</main>
Expand Down
5 changes: 2 additions & 3 deletions Website/src/app/map/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import DynamicMap from '@/components/dynmap';
import {cookies} from 'next/headers';
import {getInitData, getVehicleData} from '@/lib/data';
import {LoginDialog} from "@/components/login";
import LoginWrapper from "@/components/login_wrap";
import {InitResponse, Vehicle} from "@/lib/api.website";
import {nanToUndefined} from "@/lib/helpers";
Expand All @@ -21,7 +20,7 @@ export default async function Home({searchParams}: { searchParams: { focus?: str
init_data = (token && track_selected) ? await getInitData(token, track_id) : undefined;
server_vehicles = (token && track_selected) ? await getVehicleData(token, track_id) : [];
} catch (e) {
console.log('Catched e:', e);
console.error('Catched e:', e);
init_data = undefined;
server_vehicles = []
}
Expand All @@ -32,7 +31,7 @@ export default async function Home({searchParams}: { searchParams: { focus?: str
<LoginWrapper logged_in={token !== undefined} track_selected={track_selected} map_conf={
{
position: {lat: 54.2333, lng: 10.6024},
zoom_level: 11,
zoom_level: 11.5,
server_vehicles,
track_id,
init_data,
Expand Down
2 changes: 2 additions & 0 deletions Website/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export default function Home() {
<ul className={'list-disc list-inside my-2'}>
<li>Current vehicle positions: <Link className="text-blue-600 visited:text-purple-700"
href={'/map'}>here</Link></li>
<li>List of current vehicles: <Link className="text-blue-600 visited:text-purple-700"
href={'/list'}>here</Link></li>
<li>Add a track <Link className="text-blue-600 visited:text-purple-700"
href={'/add_track'}>here</Link></li>
<li>Select a different track <Link className="text-blue-600 visited:text-purple-700"
Expand Down
2 changes: 1 addition & 1 deletion Website/src/app/select_track/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export default function Page() {
return (
// <div className='h-full min-h-screen'>
<main className="container mx-auto max-w-2xl grow">
<div className={'bg-white p-4 rounded'}>
<div className={'bg-white dark:bg-slate-800 dark:text-white p-4 rounded'}>
<Selection/>
</div>
</main>
Expand Down
95 changes: 95 additions & 0 deletions Website/src/components/dynlist.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
"use client";

import {IMapRefreshConfig, RevalidateError} from "@/lib/types";
import {Vehicle} from "@/lib/api.website";
import useSWR from "swr";
import {batteryLevelFormatter, coordinateFormatter} from "@/lib/helpers";
import Link from "next/link";

var i = 0
const fetcher = ([url, track_id]: [url: string, track_id: number]) => {
return fetch(url, {method: 'post', body: JSON.stringify({track_id})}).then(
async (res: Response) => {
if (!res.ok) {
// console.log('not ok!');
throw new RevalidateError('Re-Fetching unsuccessful', res.status);
}
//console.log('ok')
return res;
}
).then(res => res.json())
.then(res => {
// console.log(res);
const test_vehicle: Vehicle = {
id: 0,
pos: {
lat: 54.17 + 0.05 * Math.cos(i * Math.PI / 180),
lng: 10.56 + 0.085 * Math.sin(i * Math.PI / 180)
},
heading: (i + 90) % 360,
name: 'foo',
batteryLevel: 0.5
};
// {id: 42, pos: {lat: 54.2 + 0.05 * Math.cos((i.current + 180) * Math.PI / 180), lng: 10.56 + 0.085 * Math.sin((i.current + 180) * Math.PI / 180) }, heading: i.current - 90, name: 'bar', batteryLevel: 1}
// ];
i += 5.1;
return res.concat([test_vehicle])
});
};

export default function DynamicList(props: IMapRefreshConfig) {

const {server_vehicles, track_id, logged_in} = props;
// console.log(props)

// const [vehicles, setVehicles] = useState(server_vehicles)
// const timeoutRef = useRef(undefined as NodeJS.Timeout | undefined);

const {data, error, isLoading} = useSWR((logged_in && track_id) ? ['/webapi/update', track_id] : null, fetcher, {
refreshInterval: 1000,
})

// console.log(data, error, isLoading);

const vehicles: Vehicle[] = (isLoading || error || !logged_in || !track_id) ? server_vehicles : data;
const sorted_vehicles = vehicles.sort((a, b) => a.id - b.id);

if (logged_in && error) {
if (error instanceof RevalidateError && error.statusCode == 401) {
console.log('Invalid token');
window.location.reload();
}
console.log("revalidate error", error)
}

return (
<>
<h2>{`Fahrzeuge der Strecke ${undefined}`}</h2>
<table className={'table-auto border-collapse w-full'}>
<thead>
<tr className={'my-2'}>
<th className={'mx-2 border-b-black border-b px-2'}>Name</th>
<th className={'mx-2 border-b-black border-b px-2'}>geog. Breite</th>
<th className={'mx-2 border-b-black border-b px-2'}>geog. Länge</th>
<th className={'mx-2 border-b-black border-b px-2'}>Richtung</th>
<th className={'mx-2 border-b-black border-b px-2'}>Batterieladung</th>
<th className={'mx-2 border-b-black border-b px-2'}>Auf Karte anzeigen</th>
</tr>
</thead>
<tbody>
{sorted_vehicles.map((v) => (
<tr key={v.id} className={'my-2'}>
<td className={'mx-2 px-2 text-center'}>{v.name}</td>
<td className={'mx-2 px-2 text-center'}>{coordinateFormatter.format(v.pos.lat)} N</td>
<td className={'mx-2 px-2 text-center'}>{coordinateFormatter.format(v.pos.lng)} E</td>
<td className={'mx-2 px-2 text-center'}>{v.heading ? coordinateFormatter.format(v.heading) : 'unbekannt'}</td>
<td className={'mx-2 px-2 text-center'}>{batteryLevelFormatter.format(v.batteryLevel)}</td>
<td className={'mx-2 px-2 text-center'}><Link className="text-blue-600 visited:text-purple-700" href={`/map?focus=${v.id}`}>Link</Link></td>
</tr>
))}
</tbody>
</table>
</>
)

}
24 changes: 0 additions & 24 deletions Website/src/components/layout.tsx

This file was deleted.

10 changes: 5 additions & 5 deletions Website/src/components/login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ type Url = string | UrlObject;
export default function Login({dst_url, signup}: {dst_url?: Url, signup?: boolean}) {
const pathname = usePathname() || '/';
return (
<form action="/webapi/auth" method="POST" className="grid grid-cols-2 gap-y-1 mx-1.5 items-center">
<form action="/webapi/auth" method="POST" className="grid grid-cols-2 gap-y-1 my-1.5 items-center">
<label htmlFor="username">Username:</label>
<input type="text" id="username" name="username" className="border border-gray-500 rounded" autoFocus={true} />
<input type="text" id="username" name="username" className="border border-gray-500 dark:bg-slate-700 rounded" autoFocus={true} />
<label htmlFor="password">Passwort:</label>
<input type="password" id="password" name="password" className="border border-gray-500 rounded"/>
<input type="password" id="password" name="password" className="border border-gray-500 dark:bg-slate-700 rounded"/>
<input type="hidden" value={dst_url ? format(dst_url) : pathname} name="dst_url" />
{signup && <input type={'hidden'} value={'true'} name={'signup'}/>}
<button type="submit" className="col-span-2 rounded-full bg-gray-700 text-white">Einloggen</button>
<button type="submit" className="col-span-2 rounded-full bg-slate-700 text-white dark:bg-slate-200 dark:text-black mt-1.5">Einloggen</button>
</form>
)
}
Expand All @@ -33,7 +33,7 @@ export function LoginDialog({dst_url, login_callback, children}: React.PropsWith

return (<dialog ref={dialogRef} onCancel={(event) => {
event.preventDefault();
}} className="drop-shadow-xl shadow-black backdrop:bg-gray-200/30 backdrop:backdrop-blur" >
}} className="drop-shadow-xl shadow-black bg-white dark:bg-slate-800 p-4 rounded max-w-2xl w-full dark:text-white backdrop:bg-gray-200/30 backdrop:backdrop-blur" >
{children}
<Login dst_url={dst_url} />
<Footer />
Expand Down
4 changes: 2 additions & 2 deletions Website/src/components/login_wrap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import {SelectionDialog} from "@/components/track_selection";
const LoginWrapper = ({logged_in, track_selected, map_conf, child}: {logged_in: boolean, track_selected: boolean, map_conf: IMapRefreshConfig, child: (conf: IMapRefreshConfig) => JSX.Element}) => {
const [loginState, setLogin] = useState(logged_in);

console.log('track selected', track_selected, map_conf.track_id)
// console.log('track selected', track_selected, map_conf.track_id)

return <>
{!loginState &&
<LoginDialog login_callback={setLogin}>
<p className="mb-1.5">You need to log in!</p>
</LoginDialog>}
{loginState && !track_selected && <SelectionDialog login_callback={setLogin}>
{loginState && !track_selected && <SelectionDialog>
<p className="mb-1.5">Please select a track!</p>
</SelectionDialog> }
{child({...map_conf, logged_in: loginState, setLogin: setLogin})}
Expand Down
Loading
Loading