Heliport Search, Station connection history table, reworked mission close functionality

This commit is contained in:
PxlLoewe
2025-07-21 11:43:29 -07:00
parent 9eaf3a06ed
commit 6fbb8c49a8
5 changed files with 185 additions and 114 deletions

View File

@@ -2,20 +2,26 @@
import { zodResolver } from "@hookform/resolvers/zod";
import { StationOptionalDefaultsSchema } from "@repo/db/zod";
import { useForm } from "react-hook-form";
import { BosUse, Country, Station } from "@repo/db";
import { FileText, LocateIcon, PlaneIcon } from "lucide-react";
import { BosUse, ConnectedAircraft, Country, Station, User } from "@repo/db";
import { FileText, LocateIcon, PlaneIcon, UserIcon } from "lucide-react";
import { Input } from "../../../../_components/ui/Input";
import { useState } from "react";
import { deleteStation, upsertStation } from "../action";
import { Button } from "../../../../_components/ui/Button";
import { redirect } from "next/navigation";
import toast from "react-hot-toast";
import { PaginatedTable, PaginatedTableRef } from "_components/PaginatedTable";
import { ColumnDef } from "@tanstack/react-table";
import Link from "next/link";
import { deletePilotHistory } from "(app)/admin/user/action";
import { useRef } from "react";
import { cn } from "@repo/shared-components";
export const StationForm = ({ station }: { station?: Station }) => {
const form = useForm({
resolver: zodResolver(StationOptionalDefaultsSchema),
defaultValues: station,
});
const dispoTableRef = useRef<PaginatedTableRef>(null);
// const [deleteLoading, setDeleteLoading] = useState(false);
return (
<>
@@ -27,10 +33,10 @@ export const StationForm = ({ station }: { station?: Station }) => {
})}
className="grid grid-cols-6 gap-3"
>
<div className="card bg-base-200 shadow-xl col-span-2 max-xl:col-span-6">
<div className="card bg-base-200 col-span-2 shadow-xl max-xl:col-span-6">
<div className="card-body">
<h2 className="card-title">
<FileText className="w-5 h-5" /> Allgemeines
<FileText className="h-5 w-5" /> Allgemeines
</h2>
<Input form={form} label="BOS Rufname" name="bosCallsign" className="input-sm" />
<Input
@@ -55,7 +61,7 @@ export const StationForm = ({ station }: { station?: Station }) => {
/>
<label className="form-control w-full">
<span className="label-text text-lg flex items-center gap-2">BOS Nutzung</span>
<span className="label-text flex items-center gap-2 text-lg">BOS Nutzung</span>
<select
className="input-sm select select-bordered select-sm"
{...form.register("bosUse")}
@@ -69,13 +75,13 @@ export const StationForm = ({ station }: { station?: Station }) => {
</label>
</div>
</div>
<div className="card bg-base-200 shadow-xl col-span-2 max-xl:col-span-6">
<div className="card bg-base-200 col-span-2 shadow-xl max-xl:col-span-6">
<div className="card-body">
<h2 className="card-title">
<LocateIcon className="w-5 h-5" /> Standort + Ausrüstung
<LocateIcon className="h-5 w-5" /> Standort + Ausrüstung
</h2>
<label className="form-control w-full">
<span className="label-text text-lg flex items-center gap-2">Land</span>
<span className="label-text flex items-center gap-2 text-lg">Land</span>
<select
className="input-sm select select-bordered select-sm"
{...form.register("country", {})}
@@ -94,21 +100,21 @@ export const StationForm = ({ station }: { station?: Station }) => {
name="locationStateShort"
className="input-sm"
/>
<span className="label-text text-lg flex items-center gap-2">Ausgerüstet mit:</span>
<span className="label-text flex items-center gap-2 text-lg">Ausgerüstet mit:</span>
<div className="form-control space-y-2">
<label className="label cursor-pointer flex">
<label className="label flex cursor-pointer">
<span className="flex-1 text-left">Winde</span>
<input type="checkbox" className="toggle" {...form.register("hasWinch")} />
</label>
<label className="label cursor-pointer flex">
<label className="label flex cursor-pointer">
<span className="flex-1 text-left">Nachtsicht-Gerät</span>
<input type="checkbox" className="toggle" {...form.register("hasNvg")} />
</label>
<label className="label cursor-pointer flex">
<label className="label flex cursor-pointer">
<span className="flex-1 text-left">24-Stunden Einsatzfähig</span>
<input type="checkbox" className="toggle" {...form.register("is24h")} />
</label>
<label className="label cursor-pointer flex">
<label className="label flex cursor-pointer">
<span className="flex-1 text-left">Bergetau</span>
<input type="checkbox" className="toggle" {...form.register("hasRope")} />
</label>
@@ -132,16 +138,16 @@ export const StationForm = ({ station }: { station?: Station }) => {
type="number"
step="any"
/>
<label className="label cursor-pointer flex">
<span className="text-lg flex-1 text-left">Reichweiten ausblenden</span>
<label className="label flex cursor-pointer">
<span className="flex-1 text-left text-lg">Reichweiten ausblenden</span>
<input type="checkbox" className="toggle" {...form.register("hideRangeRings")} />
</label>
</div>
</div>
<div className="card bg-base-200 shadow-xl col-span-2 max-xl:col-span-6">
<div className="card bg-base-200 col-span-2 shadow-xl max-xl:col-span-6">
<div className="card-body">
<h2 className="card-title">
<PlaneIcon className="w-5 h-5" /> Hubschrauber
<PlaneIcon className="h-5 w-5" /> Hubschrauber
</h2>
<Input form={form} label="Hubschrauber Typ" name="aircraft" className="input-sm" />
<Input
@@ -160,8 +166,8 @@ export const StationForm = ({ station }: { station?: Station }) => {
/>
</div>
</div>
<div className="card bg-base-200 shadow-xl col-span-6">
<div className="card-body ">
<div className="card bg-base-200 col-span-6 shadow-xl">
<div className="card-body">
<div className="flex w-full gap-4">
<Button
isLoading={form.formState.isSubmitting}
@@ -184,6 +190,93 @@ export const StationForm = ({ station }: { station?: Station }) => {
</div>
</div>
</div>
<div className="card bg-base-200 col-span-6 shadow-xl">
<PaginatedTable
leftOfSearch={
<div className="flex items-center gap-2 text-lg font-bold">
<UserIcon className="h-5 w-5" />
Verbundene Piloten
</div>
}
filter={{
stationId: station?.id,
}}
searchFields={["User.firstname", "User.lastname", "User.publicId"]}
prismaModel={"connectedAircraft"}
include={{ Station: true, User: true }}
columns={
[
{
accessorKey: "User.firstname",
header: "Nutzer",
cell: ({ row }) => {
return (
<Link
className="link link-hover"
href={`/admin/user/${row.original.User.id}`}
>
{row.original.User.firstname} {row.original.User.lastname} (
{row.original.User.publicId})
</Link>
);
},
},
{
accessorKey: "loginTime",
header: "Login",
cell: ({ row }) => {
return new Date(row.getValue("loginTime")).toLocaleString("de-DE");
},
},
{
accessorKey: "logoutTime",
header: "Logout",
cell: ({ row }) => {
return new Date(row.getValue("logoutTime")).toLocaleString("de-DE");
},
},
{
header: "Time Online",
cell: ({ row }) => {
if (!row.original.logoutTime) {
return <span className="text-success">Online</span>;
}
const loginTime = new Date(row.original.loginTime).getTime();
const logoutTime = new Date(row.original.logoutTime).getTime();
const timeOnline = logoutTime - loginTime;
const hours = Math.floor(timeOnline / 1000 / 60 / 60);
const minutes = Math.floor((timeOnline / 1000 / 60) % 60);
return (
<span className={cn(hours > 2 && "text-error")}>
{hours}h {minutes}min
</span>
);
},
},
{
header: "Aktionen",
cell: ({ row }) => {
return (
<div>
<button
className="btn btn-sm btn-error"
onClick={async () => {
await deletePilotHistory(row.original.id);
dispoTableRef.current?.refresh();
}}
>
löschen
</button>
</div>
);
},
},
] as ColumnDef<ConnectedAircraft & { Station: Station; User: User }>[]
}
/>
</div>
</form>
</>
);