Skip to content

Commit

Permalink
CRU operations for Vehicles and VehicleTypes
Browse files Browse the repository at this point in the history
  • Loading branch information
n1kPLV committed Jul 28, 2023
1 parent e8385a2 commit ec3c058
Show file tree
Hide file tree
Showing 16 changed files with 788 additions and 25 deletions.
63 changes: 43 additions & 20 deletions Website/src/app/add_track/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
'use client'

import {FormEvent, useRef} from "react";
import {FormEvent, useRef, useState} from "react";
import {TrackPath} from "@/lib/api.website";

export default function Home(x: any) {
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()
Expand All @@ -20,28 +22,49 @@ export default function Home(x: any) {
end: formData.get('trackEnd') as string,
}

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

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 (
<div className='h-full min-h-screen'>
<main className="container mx-auto max-w-2xl">
<div className={'bg-white p-4 rounded'}>
<form action={"#"} onSubmit={submit} ref={formRef} className={'grid grid-cols-8 gap-y-1 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"/>
<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"/>
<label htmlFor={'track'} className={'col-span-3'}>Positionsdaten (GeoJSON)</label><input id='track' name={'track'}
type={"file"} className={'col-span-5'}/>
<button type={"submit"} className="col-span-8 rounded-full bg-gray-700 text-white">Absenden</button>
</form>
</div>
</main>
</div>
<>
{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>
}
</>
)
}
2 changes: 1 addition & 1 deletion Website/src/app/components/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {actionAsyncStorage} from "next/dist/server/app-render/entry-base";

export default function Header() {

return (<header className={'flex flex-row w-full flex-initial justify-items-center mb-1.5'}>
return (<header className={'flex flex-row w-full flex-initial justify-items-center mb-1.5 p-2'}>
<p className={''}><Link href={'/'}>RailTrail Admin interface</Link></p>
<div className={'grow'}/>
<p className={''}><Link href={'/login'}>Login</Link> | <Link href={'/logout'} prefetch={false}>Logout</Link></p>
Expand Down
9 changes: 9 additions & 0 deletions Website/src/app/management/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export default function Layout({children,}: { children: React.ReactNode }) {
return (
<main className="container mx-auto max-w-2xl grow">
<div className={'bg-white dark:bg-slate-800 dark:text-white p-4 rounded'}>
{children}
</div>
</main>
);
}
174 changes: 174 additions & 0 deletions Website/src/app/management/vehicleTypes/client.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
"use client";

/*
This is a co-located client-component. It is not in the components folder, because it is unlikely to be useful anywhere
else, but also not in ´page.tsx` as we need to obtain the currently selected track id on the server.
*/

import {ChangeEventHandler, FormEventHandler, useRef, useState} from "react";
import useSWR from "swr";
import {RevalidateError} from "@/lib/types";
import {VehicleTypeCrU, VehicleTypeList} from "@/lib/api.website";
import {nanToUndefined} from "@/lib/helpers";

const fetcher = async (url: string) => {
const res = await fetch(url, {method: 'GET',});
if (!res.ok) {
// console.log('not ok!');
throw new RevalidateError('Re-Fetching unsuccessful', res.status);
}
const res_1 = await res;
const res_2: VehicleTypeList = await res_1.json();
res_2.push({uid: NaN, name: '[Neue Fahrzeugart hinzufügen]',});
return res_2;
};

export default function VehicleTypeManagement() {

// Vehicle information
const {
data: vehicleTypeList,
error: err,
isLoading,
mutate
} = useSWR('/webapi/vehicleTypes/list', fetcher)
// const vehicleList: VehicleListItem[] | undefined = isLoading ? undefined : data;

// console.log('data', vehicleList, isLoading, err);

// Form states
const [selType, setSelType] = useState('');
const [typeName, setTypeName] = useState('');
const [typeDescription, setTypeDescription] = useState('');
// const [vehicTrackers, setVehicTrackers] = useState(['']);
/** @const modified: A "dirty flag" to prevent loosing information. */
const [modified, setModified] = useState(false);

// Form submission state
const formRef = useRef(null as (null | HTMLFormElement));
const [success, setSuccess] = useState(false);
const [error, setError] = useState(undefined as string | undefined);

// Form submission function
const updateType: FormEventHandler = async (e) => {
e.preventDefault();
// const formData = new FormData(formRef.current ?? undefined);
const updatePayload: VehicleTypeCrU = {
uid: nanToUndefined(+(selType || NaN)),
name: typeName,
description: typeDescription || undefined,
}

console.log('updatePayload', updatePayload);

try {
const result = await fetch(`/webapi/vehicleTypes/update`, {
method: 'post',
body: JSON.stringify(updatePayload),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
if (result.ok) {
setSuccess(true);
setError(undefined);
// tell swr that the data on the server has probably changed.
mutate();
} 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(`Connection Error: ${e}`)
}

}

const deleteType: FormEventHandler = (e) => {
e.preventDefault();
setError('gelöscht!');
}

// select different vehicle function

const getTypeByUid = (vehicleTypeList: VehicleTypeList, uid: number) => {
for (const type of vehicleTypeList) {
if (type.uid == uid)
return type;
}
return;
}

const selectVehicle: ChangeEventHandler<HTMLSelectElement> = (e) => {
e.preventDefault()
console.log(e.target.value, typeof e.target.value);
if (modified) {
if (e.target.value != selType) {
const confirmation = confirm('Möchten Sie wirklich eine andere Fahrzeugart wählen? Ihre aktuellen Änderungen gehen verloren!')
if (!confirmation)
return;
} else
return;
}
const selectedType = vehicleTypeList ? getTypeByUid(vehicleTypeList, Number.parseInt(e.target.value, 10)) : undefined;
setSelType(e.target.value);
setTypeName(selectedType?.name ?? '');
setTypeDescription('' + (selectedType?.description ?? ''));
setModified(false);
}

return (
<form onSubmit={updateType} ref={formRef}
className={'grid grid-cols-8 gap-y-2 mx-1.5 items-center'}>
{success ? <div
className='transition ease-in col-span-8 bg-green-300 border-green-600 text-black rounded p-2 text-center'>
<div>Änderungen erfolgreich durchgeführt</div>
<button className={"rounded-full bg-gray-700 px-10 text-white"} type={'button'}
onClick={() => {
setSuccess(false);
setModified(false)
}}>Weitere Änderung durchführen
</button>
</div> : <>
<label htmlFor={'selType'} className={'col-span-3'}>Fahrzeugart:</label>
<select value={selType} onChange={selectVehicle} id={'selType'} name={'selType'}
className="col-span-5 border border-gray-500 dark:bg-slate-700 rounded">
{vehicleTypeList?.map((v) => <option key={v.uid}
value={nanToUndefined(v.uid) ?? ''}>{v.name}</option>)}
</select>
<label htmlFor={'typeName'} className={'col-span-3'}>Name:</label>
<input value={typeName} id={'typeName'} name={'typeName'}
className="col-span-5 border border-gray-500 dark:bg-slate-700 rounded"
onChange={(e) => {
setTypeName(e.target.value);
setModified(true)
}}
/>
<label htmlFor={'typeDescription'} className={'col-span-3'}>Beschreibung:</label>
<textarea value={typeDescription} id={'typeDescription'} name={'typeDescription'}
className="col-span-5 border border-gray-500 dark:bg-slate-700 rounded"
onChange={(e) => {
setTypeDescription(e.target.value);
setModified(true)
}}
/>
{error && <div
className='col-span-8 bg-red-300 border-red-600 text-black rounded p-2 text-center'>{error}</div>}
{!success && !isLoading &&
<>
<button type={"submit"} className="col-span-8 rounded-full bg-gray-700 text-white"
onSubmitCapture={updateType}>Ändern/hinzufügen
</button>
<button type={"button"} className="col-span-8 rounded-full bg-gray-700 text-white"
onClick={deleteType}>Löschen
</button>
</>
}
</>
}
</form>
);
}
5 changes: 5 additions & 0 deletions Website/src/app/management/vehicleTypes/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import VehicleTypeManagement from "./client";

export default async function Page() {
return <VehicleTypeManagement/>
}
Loading

0 comments on commit ec3c058

Please sign in to comment.