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,6 +2,39 @@ import { MissionLog, NotificationPayload, prisma } from "@repo/db";
import { io } from "index"; import { io } from "index";
import cron from "node-cron"; import cron from "node-cron";
const removeMission = async (id: number, reason: string) => {
const log: MissionLog = {
type: "completed-log",
auto: true,
timeStamp: new Date().toISOString(),
data: {},
};
const updatedMission = await prisma.mission.update({
where: {
id: id,
},
data: {
state: "finished",
missionLog: {
push: log as any,
},
},
});
io.to("dispatchers").emit("new-mission", { updatedMission });
io.to("dispatchers").emit("notification", {
type: "mission-auto-close",
status: "chron",
message: `Einsatz ${updatedMission.publicId} wurde aufgrund ${reason} geschlossen.`,
data: {
missionId: updatedMission.id,
publicMissionId: updatedMission.publicId,
},
} as NotificationPayload);
console.log(`Mission ${updatedMission.id} closed due to inactivity.`);
};
const removeClosedMissions = async () => { const removeClosedMissions = async () => {
const oldMissions = await prisma.mission.findMany({ const oldMissions = await prisma.mission.findMany({
where: { where: {
@@ -15,18 +48,6 @@ const removeClosedMissions = async () => {
const lastAlertTime = lastAlert ? new Date(lastAlert.timeStamp) : null; const lastAlertTime = lastAlert ? new Date(lastAlert.timeStamp) : null;
const aircraftsInMission = await prisma.connectedAircraft.findMany({
where: {
stationId: {
in: mission.missionStationIds,
},
},
});
const allConnectedAircraftsInIdleStatus = aircraftsInMission.every((a) =>
["1", "2", "6"].includes(a.fmsStatus),
);
const allStationsInMissionChangedFromStatus4to1Or8to1 = mission.missionStationIds.every( const allStationsInMissionChangedFromStatus4to1Or8to1 = mission.missionStationIds.every(
(stationId) => { (stationId) => {
const status4Log = (mission.missionLog as unknown as MissionLog[]).findIndex((l) => { const status4Log = (mission.missionLog as unknown as MissionLog[]).findIndex((l) => {
@@ -69,67 +90,24 @@ const removeClosedMissions = async () => {
}, },
); );
const missionHastManualReactivation = (mission.missionLog as unknown as MissionLog[]).some( const missionHasManualReactivation = (mission.missionLog as unknown as MissionLog[]).some(
(l) => l.type === "reopened-log", (l) => l.type === "reopened-log",
); );
console.log({
missionId: mission.publicId,
allConnectedAircraftsInIdleStatus,
lastAlertTime,
allStationsInMissionChangedFromStatus4to1Or8to1,
missionHastManualReactivation,
});
if (
!allConnectedAircraftsInIdleStatus // If some aircrafts are still active, do not close the mission
)
return;
const now = new Date(); if (missionHasManualReactivation) return;
if (!lastAlertTime) return; if (!lastAlertTime) return;
// Case 1: Forgotten Mission, last alert more than 3 Hours ago // Case 1: Forgotten Mission, last alert more than 3 Hours ago
// Case 2: All stations in mission changed from status 4 to 1 or from status 8 to 1 const now = new Date();
if ( if (now.getTime() - lastAlertTime.getTime() > 1000 * 60 * 180)
!( return removeMission(mission.id, "inaktivität");
now.getTime() - lastAlertTime.getTime() > 1000 * 60 * 180 ||
allStationsInMissionChangedFromStatus4to1Or8to1
) ||
missionHastManualReactivation
)
return;
const log: MissionLog = { // Case 2: All stations in mission changed from status 4 to 1/6 or from status 8 to 1/6
type: "completed-log", if (allStationsInMissionChangedFromStatus4to1Or8to1)
auto: true, return removeMission(mission.id, "dem freimelden aller Stationen");
timeStamp: new Date().toISOString(),
data: {},
};
const updatedMission = await prisma.mission.update({
where: {
id: mission.id,
},
data: {
state: "finished",
missionLog: {
push: log as any,
},
},
});
io.to("dispatchers").emit("new-mission", { updatedMission });
io.to("dispatchers").emit("notification", {
type: "mission-auto-close",
status: "chron",
message: `Einsatz ${updatedMission.publicId} wurde aufgrund ${allStationsInMissionChangedFromStatus4to1Or8to1 ? "des Freimeldens aller Stationen" : "von Inaktivität"} geschlossen.`,
data: {
missionId: updatedMission.id,
publicMissionId: updatedMission.publicId,
},
} as NotificationPayload);
console.log(`Mission ${mission.id} closed due to inactivity.`);
}); });
}; };
const removeConnectedAircrafts = async () => { const removeConnectedAircrafts = async () => {
const connectedAircrafts = await prisma.connectedAircraft.findMany({ const connectedAircrafts = await prisma.connectedAircraft.findMany({
where: { where: {

View File

@@ -225,7 +225,7 @@ export const SettingsBtn = () => {
<p className="label mt-2 w-full"> <p className="label mt-2 w-full">
<Link <Link
href="https://docs.virtualairrescue.com/docs/Leitstelle/App-Alarmierung#download" href="https://docs.virtualairrescue.com/pilotenbereich/app-alarmierung.html#download"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="link link-hover link-primary" className="link link-hover link-primary"

View File

@@ -11,7 +11,7 @@ const page = () => {
<PaginatedTable <PaginatedTable
stickyHeaders stickyHeaders
prismaModel="heliport" prismaModel="heliport"
searchFields={["siteName", "info", "hospital"]} searchFields={["siteName", "info", "hospital", "designator"]}
columns={ columns={
[ [
{ {
@@ -45,11 +45,11 @@ const page = () => {
} }
leftOfSearch={ leftOfSearch={
<span className="flex items-center gap-2"> <span className="flex items-center gap-2">
<DatabaseBackupIcon className="w-5 h-5" /> Heliports <DatabaseBackupIcon className="h-5 w-5" /> Heliports
</span> </span>
} }
rightOfSearch={ rightOfSearch={
<p className="text-2xl font-semibold text-left flex items-center gap-2 justify-between"> <p className="flex items-center justify-between gap-2 text-left text-2xl font-semibold">
<Link href={"/admin/heliport/new"}> <Link href={"/admin/heliport/new"}>
<button className="btn btn-sm btn-outline btn-primary">Erstellen</button> <button className="btn btn-sm btn-outline btn-primary">Erstellen</button>
</Link> </Link>

View File

@@ -2,20 +2,26 @@
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { StationOptionalDefaultsSchema } from "@repo/db/zod"; import { StationOptionalDefaultsSchema } from "@repo/db/zod";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { BosUse, Country, Station } from "@repo/db"; import { BosUse, ConnectedAircraft, Country, Station, User } from "@repo/db";
import { FileText, LocateIcon, PlaneIcon } from "lucide-react"; import { FileText, LocateIcon, PlaneIcon, UserIcon } from "lucide-react";
import { Input } from "../../../../_components/ui/Input"; import { Input } from "../../../../_components/ui/Input";
import { useState } from "react";
import { deleteStation, upsertStation } from "../action"; import { deleteStation, upsertStation } from "../action";
import { Button } from "../../../../_components/ui/Button"; import { Button } from "../../../../_components/ui/Button";
import { redirect } from "next/navigation"; import { redirect } from "next/navigation";
import toast from "react-hot-toast"; 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 }) => { export const StationForm = ({ station }: { station?: Station }) => {
const form = useForm({ const form = useForm({
resolver: zodResolver(StationOptionalDefaultsSchema), resolver: zodResolver(StationOptionalDefaultsSchema),
defaultValues: station, defaultValues: station,
}); });
const dispoTableRef = useRef<PaginatedTableRef>(null);
// const [deleteLoading, setDeleteLoading] = useState(false); // const [deleteLoading, setDeleteLoading] = useState(false);
return ( return (
<> <>
@@ -27,10 +33,10 @@ export const StationForm = ({ station }: { station?: Station }) => {
})} })}
className="grid grid-cols-6 gap-3" 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"> <div className="card-body">
<h2 className="card-title"> <h2 className="card-title">
<FileText className="w-5 h-5" /> Allgemeines <FileText className="h-5 w-5" /> Allgemeines
</h2> </h2>
<Input form={form} label="BOS Rufname" name="bosCallsign" className="input-sm" /> <Input form={form} label="BOS Rufname" name="bosCallsign" className="input-sm" />
<Input <Input
@@ -55,7 +61,7 @@ export const StationForm = ({ station }: { station?: Station }) => {
/> />
<label className="form-control w-full"> <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 <select
className="input-sm select select-bordered select-sm" className="input-sm select select-bordered select-sm"
{...form.register("bosUse")} {...form.register("bosUse")}
@@ -69,13 +75,13 @@ export const StationForm = ({ station }: { station?: Station }) => {
</label> </label>
</div> </div>
</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"> <div className="card-body">
<h2 className="card-title"> <h2 className="card-title">
<LocateIcon className="w-5 h-5" /> Standort + Ausrüstung <LocateIcon className="h-5 w-5" /> Standort + Ausrüstung
</h2> </h2>
<label className="form-control w-full"> <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 <select
className="input-sm select select-bordered select-sm" className="input-sm select select-bordered select-sm"
{...form.register("country", {})} {...form.register("country", {})}
@@ -94,21 +100,21 @@ export const StationForm = ({ station }: { station?: Station }) => {
name="locationStateShort" name="locationStateShort"
className="input-sm" 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"> <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> <span className="flex-1 text-left">Winde</span>
<input type="checkbox" className="toggle" {...form.register("hasWinch")} /> <input type="checkbox" className="toggle" {...form.register("hasWinch")} />
</label> </label>
<label className="label cursor-pointer flex"> <label className="label flex cursor-pointer">
<span className="flex-1 text-left">Nachtsicht-Gerät</span> <span className="flex-1 text-left">Nachtsicht-Gerät</span>
<input type="checkbox" className="toggle" {...form.register("hasNvg")} /> <input type="checkbox" className="toggle" {...form.register("hasNvg")} />
</label> </label>
<label className="label cursor-pointer flex"> <label className="label flex cursor-pointer">
<span className="flex-1 text-left">24-Stunden Einsatzfähig</span> <span className="flex-1 text-left">24-Stunden Einsatzfähig</span>
<input type="checkbox" className="toggle" {...form.register("is24h")} /> <input type="checkbox" className="toggle" {...form.register("is24h")} />
</label> </label>
<label className="label cursor-pointer flex"> <label className="label flex cursor-pointer">
<span className="flex-1 text-left">Bergetau</span> <span className="flex-1 text-left">Bergetau</span>
<input type="checkbox" className="toggle" {...form.register("hasRope")} /> <input type="checkbox" className="toggle" {...form.register("hasRope")} />
</label> </label>
@@ -132,16 +138,16 @@ export const StationForm = ({ station }: { station?: Station }) => {
type="number" type="number"
step="any" step="any"
/> />
<label className="label cursor-pointer flex"> <label className="label flex cursor-pointer">
<span className="text-lg flex-1 text-left">Reichweiten ausblenden</span> <span className="flex-1 text-left text-lg">Reichweiten ausblenden</span>
<input type="checkbox" className="toggle" {...form.register("hideRangeRings")} /> <input type="checkbox" className="toggle" {...form.register("hideRangeRings")} />
</label> </label>
</div> </div>
</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"> <div className="card-body">
<h2 className="card-title"> <h2 className="card-title">
<PlaneIcon className="w-5 h-5" /> Hubschrauber <PlaneIcon className="h-5 w-5" /> Hubschrauber
</h2> </h2>
<Input form={form} label="Hubschrauber Typ" name="aircraft" className="input-sm" /> <Input form={form} label="Hubschrauber Typ" name="aircraft" className="input-sm" />
<Input <Input
@@ -160,7 +166,7 @@ export const StationForm = ({ station }: { station?: Station }) => {
/> />
</div> </div>
</div> </div>
<div className="card bg-base-200 shadow-xl col-span-6"> <div className="card bg-base-200 col-span-6 shadow-xl">
<div className="card-body"> <div className="card-body">
<div className="flex w-full gap-4"> <div className="flex w-full gap-4">
<Button <Button
@@ -184,6 +190,93 @@ export const StationForm = ({ station }: { station?: Station }) => {
</div> </div>
</div> </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> </form>
</> </>
); );

View File

@@ -10,7 +10,7 @@ import {
Station, Station,
User, User,
} from "@repo/db"; } from "@repo/db";
import { useRef, useState } from "react"; import { useRef } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { import {
deleteDispoHistory, deleteDispoHistory,
@@ -84,11 +84,11 @@ export const ProfileForm: React.FC<ProfileFormProps> = ({ user }: ProfileFormPro
})} })}
> >
<h2 className="card-title"> <h2 className="card-title">
<MixerHorizontalIcon className="w-5 h-5" /> User bearbeiten <MixerHorizontalIcon className="h-5 w-5" /> User bearbeiten
</h2> </h2>
<div className="text-left"> <div className="text-left">
<label className="floating-label w-full mb-5 mt-5"> <label className="floating-label mb-5 mt-5 w-full">
<span className="text-lg flex items-center gap-2"> <span className="flex items-center gap-2 text-lg">
<PersonIcon /> Vorname <PersonIcon /> Vorname
</span> </span>
<input <input
@@ -102,8 +102,8 @@ export const ProfileForm: React.FC<ProfileFormProps> = ({ user }: ProfileFormPro
{form.formState.errors.firstname && ( {form.formState.errors.firstname && (
<p className="text-error">{form.formState.errors.firstname.message}</p> <p className="text-error">{form.formState.errors.firstname.message}</p>
)} )}
<label className="floating-label w-full mb-5"> <label className="floating-label mb-5 w-full">
<span className="text-lg flex items-center gap-2"> <span className="flex items-center gap-2 text-lg">
<PersonIcon /> Nachname <PersonIcon /> Nachname
</span> </span>
<input <input
@@ -120,13 +120,13 @@ export const ProfileForm: React.FC<ProfileFormProps> = ({ user }: ProfileFormPro
{session.data?.user.permissions.includes("ADMIN_USER_ADVANCED") && ( {session.data?.user.permissions.includes("ADMIN_USER_ADVANCED") && (
<> <>
<label className="floating-label w-full"> <label className="floating-label w-full">
<span className="text-lg flex items-center gap-2"> <span className="flex items-center gap-2 text-lg">
<EnvelopeClosedIcon /> E-Mail <EnvelopeClosedIcon /> E-Mail
</span> </span>
<input <input
{...form.register("email")} {...form.register("email")}
type="text" type="text"
className="input input-bordered w-full mb-2" className="input input-bordered mb-2 w-full"
defaultValue={user?.email} defaultValue={user?.email}
placeholder="E-Mail" placeholder="E-Mail"
/> />
@@ -166,7 +166,7 @@ export const ProfileForm: React.FC<ProfileFormProps> = ({ user }: ProfileFormPro
{session.data?.user.permissions.includes("ADMIN_USER_ADVANCED") && ( {session.data?.user.permissions.includes("ADMIN_USER_ADVANCED") && (
<div className="mt-2 space-y-1"> <div className="mt-2 space-y-1">
<p className="text-gray-400">Berechtigung Schnellauswahl</p> <p className="text-gray-400">Berechtigung Schnellauswahl</p>
<div className="flex gap-2 justify-evenly"> <div className="flex justify-evenly gap-2">
<button <button
type="button" type="button"
className="btn btn-sm btn-outline" className="btn btn-sm btn-outline"
@@ -251,7 +251,7 @@ export const ConnectionHistory: React.FC<{ user: User }> = ({ user }: { user: Us
<div className="card-body flex-row flex-wrap"> <div className="card-body flex-row flex-wrap">
<div className="flex-1"> <div className="flex-1">
<h2 className="card-title"> <h2 className="card-title">
<MixerHorizontalIcon className="w-5 h-5" /> Dispo-Verbindungs Historie <MixerHorizontalIcon className="h-5 w-5" /> Dispo-Verbindungs Historie
</h2> </h2>
<PaginatedTable <PaginatedTable
ref={dispoTableRef} ref={dispoTableRef}
@@ -313,7 +313,7 @@ export const ConnectionHistory: React.FC<{ user: User }> = ({ user }: { user: Us
</div> </div>
<div className="flex-1"> <div className="flex-1">
<h2 className="card-title"> <h2 className="card-title">
<PlaneIcon className="w-5 h-5" /> Pilot-Verbindungs Historie <PlaneIcon className="h-5 w-5" /> Pilot-Verbindungs Historie
</h2> </h2>
<PaginatedTable <PaginatedTable
ref={dispoTableRef} ref={dispoTableRef}
@@ -396,7 +396,7 @@ export const UserPenalties = ({ user }: { user: User }) => {
<div className="card-body"> <div className="card-body">
<h2 className="card-title flex justify-between"> <h2 className="card-title flex justify-between">
<span className="flex items-center gap-2"> <span className="flex items-center gap-2">
<ExclamationTriangleIcon className="w-5 h-5" /> Audit-log <ExclamationTriangleIcon className="h-5 w-5" /> Audit-log
</span> </span>
<div className="flex gap-2"> <div className="flex gap-2">
<PenaltyDropdown <PenaltyDropdown
@@ -478,7 +478,7 @@ export const UserReports = ({ user }: { user: User }) => {
return ( return (
<div className="card-body"> <div className="card-body">
<h2 className="card-title"> <h2 className="card-title">
<ExclamationTriangleIcon className="w-5 h-5" /> Nutzer Reports <ExclamationTriangleIcon className="h-5 w-5" /> Nutzer Reports
</h2> </h2>
<PaginatedTable <PaginatedTable
prismaModel="report" prismaModel="report"
@@ -528,10 +528,10 @@ export const AdminForm = ({
return ( return (
<div className="card-body"> <div className="card-body">
<h2 className="card-title"> <h2 className="card-title">
<LightningBoltIcon className="w-5 h-5" /> Administration <LightningBoltIcon className="h-5 w-5" /> Administration
</h2> </h2>
<div className="text-left"> <div className="text-left">
<div className="card-actions pt-6 flex flex-wrap gap-2"> <div className="card-actions flex flex-wrap gap-2 pt-6">
<Button <Button
onClick={async () => { onClick={async () => {
const { password } = await resetPassword(user.id); const { password } = await resetPassword(user.id);
@@ -547,13 +547,13 @@ export const AdminForm = ({
}, },
); );
}} }}
className="btn-sm flex-1 min-w-[250px] btn-outline btn-success" className="btn-sm btn-outline btn-success min-w-[250px] flex-1"
> >
<LockOpen1Icon /> Passwort zurücksetzen <LockOpen1Icon /> Passwort zurücksetzen
</Button> </Button>
{session?.user.permissions.includes("ADMIN_USER_ADVANCED") && ( {session?.user.permissions.includes("ADMIN_USER_ADVANCED") && (
<div <div
className="tooltip flex-1 min-w-[250px] tooltip-warning" className="tooltip tooltip-warning min-w-[250px] flex-1"
data-tip="Dies löscht den Nutzer sofort, außerdem alle Reports, Verbindungs-Verlauf und Chat-Nachrichten" data-tip="Dies löscht den Nutzer sofort, außerdem alle Reports, Verbindungs-Verlauf und Chat-Nachrichten"
> >
<Button <Button
@@ -581,14 +581,14 @@ export const AdminForm = ({
router.refresh(); router.refresh();
}} }}
role="submit" role="submit"
className="btn-sm flex-1 min-w-[250px] btn-outline btn-warning" className="btn-sm btn-outline btn-warning min-w-[250px] flex-1"
> >
<HobbyKnifeIcon /> Account entsperren <HobbyKnifeIcon /> Account entsperren
</Button> </Button>
)} )}
{discordAccount && ( {discordAccount && (
<div <div
className="tooltip flex-1 min-w-[250px]" className="tooltip min-w-[250px] flex-1"
data-tip={`Name: ${discordAccount.username}`} data-tip={`Name: ${discordAccount.username}`}
> >
<Button <Button
@@ -604,7 +604,7 @@ export const AdminForm = ({
}, },
}); });
}} }}
className="btn-sm w-full btn-outline btn-info" className="btn-sm btn-outline btn-info w-full"
> >
<DiscordLogoIcon /> Name und Berechtigungen setzen <DiscordLogoIcon /> Name und Berechtigungen setzen
</Button> </Button>
@@ -613,12 +613,12 @@ export const AdminForm = ({
</div> </div>
</div> </div>
<h2 className="card-title"> <h2 className="card-title">
<ChartBarBigIcon className="w-5 h-5" /> Aktivität <ChartBarBigIcon className="h-5 w-5" /> Aktivität
</h2> </h2>
<div className="stats flex"> <div className="stats flex">
<div className="stat"> <div className="stat">
<div className="stat-figure text-primary"> <div className="stat-figure text-primary">
<LightningBoltIcon className="w-8 h-8" /> <LightningBoltIcon className="h-8 w-8" />
</div> </div>
<div className="stat-value text-primary"> <div className="stat-value text-primary">
{dispoTime.hours}h {dispoTime.minutes}min {dispoTime.hours}h {dispoTime.minutes}min
@@ -630,7 +630,7 @@ export const AdminForm = ({
</div> </div>
<div className="stat"> <div className="stat">
<div className="stat-figure text-primary"> <div className="stat-figure text-primary">
<PlaneIcon className="w-8 h-8" /> <PlaneIcon className="h-8 w-8" />
</div> </div>
<div className="stat-value text-primary"> <div className="stat-value text-primary">
{pilotTime.hours}h {pilotTime.minutes}min {pilotTime.hours}h {pilotTime.minutes}min
@@ -642,12 +642,12 @@ export const AdminForm = ({
</div> </div>
</div> </div>
<h2 className="card-title"> <h2 className="card-title">
<ExclamationTriangleIcon className="w-5 h-5" /> Reports <ExclamationTriangleIcon className="h-5 w-5" /> Reports
</h2> </h2>
<div className="stats flex"> <div className="stats flex">
<div className="stat"> <div className="stat">
<div className="stat-figure text-primary"> <div className="stat-figure text-primary">
<ExclamationTriangleIcon className="w-8 h-8" /> <ExclamationTriangleIcon className="h-8 w-8" />
</div> </div>
<div className={cn("stat-value text-primary", reports.open && "text-warning")}> <div className={cn("stat-value text-primary", reports.open && "text-warning")}>
{reports.open} {reports.open}
@@ -656,7 +656,7 @@ export const AdminForm = ({
</div> </div>
<div className="stat"> <div className="stat">
<div className="stat-figure text-primary"> <div className="stat-figure text-primary">
<Timer className="w-8 h-8" /> <Timer className="h-8 w-8" />
</div> </div>
<div className="stat-value text-primary">{reports.total60Days}</div> <div className="stat-value text-primary">{reports.total60Days}</div>
<div className="stat-title">in den letzten 60 Tagen</div> <div className="stat-title">in den letzten 60 Tagen</div>