changes pilot socket to reperate pilto socket, added pilot stats
This commit is contained in:
@@ -1,191 +0,0 @@
|
||||
"use server";
|
||||
|
||||
import { prisma } from "@repo/db";
|
||||
import { getServerSession } from "api/auth/[...nextauth]/auth";
|
||||
import { PlaneIcon } from "lucide-react";
|
||||
|
||||
export const PilotStats = async () => {
|
||||
return (
|
||||
<div className="stats shadow">
|
||||
<div className="stat">
|
||||
<div className="stat-figure text-primary">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
className="inline-block h-8 w-8 stroke-current"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="stat-title">Einsätze geflogen</div>
|
||||
<div className="stat-value text-primary">127</div>
|
||||
<div className="stat-desc">Du bist damit unter den top 5%!</div>
|
||||
</div>
|
||||
|
||||
<div className="stat">
|
||||
<div className="stat-figure text-secondary">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
className="inline-block h-8 w-8 stroke-current"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M13 10V3L4 14h7v7l9-11h-7z"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="stat-title">Pilot Login Zeit</div>
|
||||
<div className="stat-value text-secondary">35h 12m</div>
|
||||
<div className="stat-desc">Mehr als 58% aller anderen User!</div>
|
||||
</div>
|
||||
|
||||
<div className="stat">
|
||||
<div className="stat-figure text-info">
|
||||
<PlaneIcon className="w-8 h-8" />
|
||||
</div>
|
||||
<div className="stat-value text-info">Christoph 31</div>
|
||||
<div className="stat-title">
|
||||
War bisher dein Rettungsmittel der Wahl
|
||||
</div>
|
||||
<div className="stat-desc text-secondary">
|
||||
87 Stationen warten noch auf dich!
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const DispoStats = async () => {
|
||||
const session = await getServerSession();
|
||||
if (!session) return null;
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: session.user.id },
|
||||
});
|
||||
|
||||
const dispoSessions = await prisma.connectedDispatcher.findMany({
|
||||
where: {
|
||||
userId: user?.id,
|
||||
logoutTime: {
|
||||
not: null,
|
||||
},
|
||||
},
|
||||
select: {
|
||||
loginTime: true,
|
||||
logoutTime: true,
|
||||
},
|
||||
});
|
||||
|
||||
const mostDispatchedStationIds = await prisma.mission.groupBy({
|
||||
where: {
|
||||
createdUserId: user?.id,
|
||||
},
|
||||
by: ["missionStationIds"],
|
||||
orderBy: {
|
||||
_count: {
|
||||
missionStationIds: "desc",
|
||||
},
|
||||
},
|
||||
take: 1,
|
||||
_count: {
|
||||
missionStationIds: true,
|
||||
},
|
||||
});
|
||||
|
||||
let mostDispatchedStation = null;
|
||||
|
||||
if (mostDispatchedStationIds[0]?.missionStationIds[0]) {
|
||||
mostDispatchedStation = await prisma.station.findUnique({
|
||||
where: {
|
||||
id: parseInt(mostDispatchedStationIds[0]?.missionStationIds[0]),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const totalDispatchedMissions = await prisma.mission.count({
|
||||
where: {
|
||||
createdUserId: user?.id,
|
||||
},
|
||||
});
|
||||
|
||||
const totalDispoTime = dispoSessions.reduce((acc, session) => {
|
||||
const logoffTime = new Date(session.logoutTime!).getTime();
|
||||
const logonTime = new Date(session.loginTime).getTime();
|
||||
return acc + (logoffTime - logonTime);
|
||||
}, 0);
|
||||
|
||||
const hours = Math.floor(totalDispoTime / (1000 * 60 * 60));
|
||||
const minutes = Math.floor((totalDispoTime % (1000 * 60 * 60)) / (1000 * 60));
|
||||
|
||||
return (
|
||||
<div className="stats shadow">
|
||||
<div className="stat">
|
||||
<div className="stat-figure text-primary">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
className="inline-block h-8 w-8 stroke-current"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="stat-title">Einsätze disponiert</div>
|
||||
<div className="stat-value text-primary">{totalDispatchedMissions}</div>
|
||||
<div className="stat-desc">Du bist damit unter den top 9%!</div>
|
||||
</div>
|
||||
|
||||
<div className="stat">
|
||||
<div className="stat-figure text-secondary">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
className="inline-block h-8 w-8 stroke-current"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M13 10V3L4 14h7v7l9-11h-7z"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="stat-title">Disponent Login Zeit</div>
|
||||
<div className="stat-value text-secondary">
|
||||
{hours}h {minutes}m
|
||||
</div>
|
||||
<div className="stat-desc">Mehr als 69% aller anderen User!</div>
|
||||
</div>
|
||||
|
||||
{mostDispatchedStation && (
|
||||
<div className="stat">
|
||||
<div className="stat-figure text-info">
|
||||
<PlaneIcon className="w-8 h-8" />
|
||||
</div>
|
||||
<div className="stat-value text-info">
|
||||
{mostDispatchedStation?.bosCallsign}
|
||||
</div>
|
||||
<div className="stat-title">Wurde von dir am meisten Disponiert</div>
|
||||
<div className="stat-desc text-secondary">
|
||||
{mostDispatchedStationIds[0]?._count.missionStationIds} Einsätze
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,7 +1,292 @@
|
||||
/* import { useState } from "react";
|
||||
import { StatsToggle } from "./StatsToggle"; */
|
||||
import { StatsToggle } from "(app)/_components/StatsToggle";
|
||||
import { DispoStats, PilotStats } from "./PilotDispoStats";
|
||||
|
||||
import { prisma } from "@repo/db";
|
||||
import { getServerSession } from "api/auth/[...nextauth]/auth";
|
||||
import { PlaneIcon } from "lucide-react";
|
||||
|
||||
export const PilotStats = async () => {
|
||||
const session = await getServerSession();
|
||||
if (!session) return null;
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: session.user.id },
|
||||
});
|
||||
|
||||
const mostFlownStationsIds = await prisma.missionOnStationUsers.groupBy({
|
||||
where: {
|
||||
userId: user?.id,
|
||||
},
|
||||
by: ["stationId"],
|
||||
orderBy: {
|
||||
_count: {
|
||||
stationId: "desc",
|
||||
},
|
||||
},
|
||||
take: 1,
|
||||
_count: {
|
||||
stationId: true,
|
||||
},
|
||||
});
|
||||
|
||||
let mostFlownStation = null;
|
||||
|
||||
if (mostFlownStationsIds[0]?.stationId) {
|
||||
mostFlownStation = await prisma.station.findUnique({
|
||||
where: {
|
||||
id: mostFlownStationsIds[0]?.stationId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const dispoSessions = await prisma.connectedAircraft.findMany({
|
||||
where: {
|
||||
userId: user?.id,
|
||||
logoutTime: {
|
||||
not: null,
|
||||
},
|
||||
},
|
||||
select: {
|
||||
loginTime: true,
|
||||
logoutTime: true,
|
||||
},
|
||||
});
|
||||
|
||||
const totalFlownMissions = await prisma.missionOnStationUsers.count({
|
||||
where: {
|
||||
userId: user?.id,
|
||||
},
|
||||
});
|
||||
|
||||
// Get the user's rank by missions flown
|
||||
const missionsFlownRanks = await prisma.missionOnStationUsers.groupBy({
|
||||
by: ["userId"],
|
||||
_count: { userId: true },
|
||||
orderBy: { _count: { userId: "desc" } },
|
||||
});
|
||||
|
||||
const ownRankMissionsFlown =
|
||||
missionsFlownRanks.findIndex((rank) => rank.userId === user?.id) + 1;
|
||||
|
||||
const totalUserCount = await prisma.user.count({
|
||||
where: {
|
||||
NOT: {
|
||||
id: user?.id,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const totalStationsCount = await prisma.station.count();
|
||||
|
||||
const unflownStationsCount = totalStationsCount - mostFlownStationsIds.length;
|
||||
|
||||
const totalPilotTime = dispoSessions.reduce((acc, session) => {
|
||||
const logoffTime = new Date(session.logoutTime!).getTime();
|
||||
const logonTime = new Date(session.loginTime).getTime();
|
||||
return acc + (logoffTime - logonTime);
|
||||
}, 0);
|
||||
|
||||
const hours = Math.floor(totalPilotTime / (1000 * 60 * 60));
|
||||
const minutes = Math.floor((totalPilotTime % (1000 * 60 * 60)) / (1000 * 60));
|
||||
|
||||
return (
|
||||
<div className="stats shadow">
|
||||
<div className="stat">
|
||||
<div className="stat-figure text-primary">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
className="inline-block h-8 w-8 stroke-current"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="stat-title">Einsätze geflogen</div>
|
||||
<div className="stat-value text-primary">{totalFlownMissions}</div>
|
||||
<div className="stat-desc">
|
||||
Du bist damit unter den top{" "}
|
||||
{((ownRankMissionsFlown * 100) / totalUserCount).toFixed(0)}%!
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="stat">
|
||||
<div className="stat-figure text-secondary">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
className="inline-block h-8 w-8 stroke-current"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M13 10V3L4 14h7v7l9-11h-7z"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="stat-title">Pilot Login Zeit</div>
|
||||
<div className="stat-value text-secondary">
|
||||
{hours}h {minutes}min
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{mostFlownStation && (
|
||||
<div className="stat">
|
||||
<div className="stat-figure text-info">
|
||||
<PlaneIcon className="w-8 h-8" />
|
||||
</div>
|
||||
<div className="stat-value text-info">
|
||||
{mostFlownStation?.bosCallsign}
|
||||
</div>
|
||||
<div className="stat-title">
|
||||
War bisher dein Rettungsmittel der Wahl
|
||||
</div>
|
||||
{unflownStationsCount > 0 && (
|
||||
<div className="stat-desc text-secondary">
|
||||
{unflownStationsCount}{" "}
|
||||
{unflownStationsCount > 1 ? "Stationen" : "Station"} warten noch
|
||||
auf dich!
|
||||
</div>
|
||||
)}
|
||||
{unflownStationsCount === 0 && (
|
||||
<div className="stat-desc text-secondary">
|
||||
Du hast alle Stationen geflogen! Krass...
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const DispoStats = async () => {
|
||||
const session = await getServerSession();
|
||||
if (!session) return null;
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: session.user.id },
|
||||
});
|
||||
|
||||
const dispoSessions = await prisma.connectedDispatcher.findMany({
|
||||
where: {
|
||||
userId: user?.id,
|
||||
logoutTime: {
|
||||
not: null,
|
||||
},
|
||||
},
|
||||
select: {
|
||||
loginTime: true,
|
||||
logoutTime: true,
|
||||
},
|
||||
});
|
||||
|
||||
const mostDispatchedStationIds = await prisma.mission.groupBy({
|
||||
where: {
|
||||
createdUserId: user?.id,
|
||||
},
|
||||
by: ["missionStationIds"],
|
||||
orderBy: {
|
||||
_count: {
|
||||
missionStationIds: "desc",
|
||||
},
|
||||
},
|
||||
take: 1,
|
||||
_count: {
|
||||
missionStationIds: true,
|
||||
},
|
||||
});
|
||||
|
||||
let mostDispatchedStation = null;
|
||||
|
||||
if (mostDispatchedStationIds[0]?.missionStationIds[0]) {
|
||||
mostDispatchedStation = await prisma.station.findUnique({
|
||||
where: {
|
||||
id: mostDispatchedStationIds[0]?.missionStationIds[0],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const totalDispatchedMissions = await prisma.mission.count({
|
||||
where: {
|
||||
createdUserId: user?.id,
|
||||
},
|
||||
});
|
||||
|
||||
const totalDispoTime = dispoSessions.reduce((acc, session) => {
|
||||
const logoffTime = new Date(session.logoutTime!).getTime();
|
||||
const logonTime = new Date(session.loginTime).getTime();
|
||||
return acc + (logoffTime - logonTime);
|
||||
}, 0);
|
||||
|
||||
const hours = Math.floor(totalDispoTime / (1000 * 60 * 60));
|
||||
const minutes = Math.floor((totalDispoTime % (1000 * 60 * 60)) / (1000 * 60));
|
||||
|
||||
return (
|
||||
<div className="stats shadow">
|
||||
<div className="stat">
|
||||
<div className="stat-figure text-primary">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
className="inline-block h-8 w-8 stroke-current"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="stat-title">Einsätze disponiert</div>
|
||||
<div className="stat-value text-primary">{totalDispatchedMissions}</div>
|
||||
<div className="stat-desc">Du bist damit unter den top 9%!</div>
|
||||
</div>
|
||||
|
||||
<div className="stat">
|
||||
<div className="stat-figure text-secondary">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
className="inline-block h-8 w-8 stroke-current"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M13 10V3L4 14h7v7l9-11h-7z"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="stat-title">Disponent Login Zeit</div>
|
||||
<div className="stat-value text-secondary">
|
||||
{hours}h {minutes}m
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{mostDispatchedStation && (
|
||||
<div className="stat">
|
||||
<div className="stat-figure text-info">
|
||||
<PlaneIcon className="w-8 h-8" />
|
||||
</div>
|
||||
<div className="stat-value text-info">
|
||||
{mostDispatchedStation?.bosCallsign}
|
||||
</div>
|
||||
<div className="stat-title">Wurde von dir am meisten Disponiert</div>
|
||||
<div className="stat-desc text-secondary">
|
||||
{mostDispatchedStationIds[0]?._count.missionStationIds} Einsätze
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Stats = ({ stats }: { stats: "pilot" | "dispo" }) => {
|
||||
return (
|
||||
|
||||
@@ -1,9 +1,22 @@
|
||||
"use client";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { BADGES, PERMISSION, Report, User } from "@repo/db";
|
||||
import {
|
||||
BADGES,
|
||||
ConnectedAircraft,
|
||||
ConnectedDispatcher,
|
||||
PERMISSION,
|
||||
Report,
|
||||
Station,
|
||||
User,
|
||||
} from "@repo/db";
|
||||
import { useRef, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { deleteDispoHistory, editUser, resetPassword } from "../../action";
|
||||
import {
|
||||
deleteDispoHistory,
|
||||
deletePilotHistory,
|
||||
editUser,
|
||||
resetPassword,
|
||||
} from "../../action";
|
||||
import { toast } from "react-hot-toast";
|
||||
import {
|
||||
PersonIcon,
|
||||
@@ -166,59 +179,60 @@ export const ConnectionHistory: React.FC<{ user: User }> = ({
|
||||
userId: user.id,
|
||||
}}
|
||||
prismaModel={"connectedDispatcher"}
|
||||
columns={[
|
||||
{
|
||||
accessorKey: "loginTime",
|
||||
header: "Login",
|
||||
cell: ({ row }) => {
|
||||
return new Date(row.getValue("loginTime")).toLocaleString(
|
||||
"de-DE",
|
||||
);
|
||||
columns={
|
||||
[
|
||||
{
|
||||
accessorKey: "loginTime",
|
||||
header: "Login",
|
||||
cell: ({ row }) => {
|
||||
return new Date(row.getValue("loginTime")).toLocaleString(
|
||||
"de-DE",
|
||||
);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "Time Online",
|
||||
cell: ({ row }) => {
|
||||
console.log(row.original);
|
||||
const loginTime = new Date(row.original.loginTime).getTime();
|
||||
const logoutTime = new Date(
|
||||
(row.original as any).logoutTime,
|
||||
).getTime();
|
||||
const timeOnline = logoutTime - loginTime;
|
||||
{
|
||||
header: "Time Online",
|
||||
cell: ({ row }) => {
|
||||
if (row.original.logoutTime == null) {
|
||||
return <span className="text-success">Online</span>;
|
||||
}
|
||||
const loginTime = new Date(row.original.loginTime).getTime();
|
||||
const logoutTime = new Date(
|
||||
row.original.logoutTime,
|
||||
).getTime();
|
||||
|
||||
const hours = Math.floor(timeOnline / 1000 / 60 / 60);
|
||||
const minutes = Math.floor((timeOnline / 1000 / 60) % 60);
|
||||
const timeOnline = logoutTime - loginTime;
|
||||
|
||||
if ((row.original as any).logoutTime == null) {
|
||||
return <span className="text-success">Online</span>;
|
||||
}
|
||||
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>
|
||||
);
|
||||
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 deleteDispoHistory((row.original as any).id);
|
||||
dispoTableRef.current?.refresh();
|
||||
}}
|
||||
>
|
||||
löschen
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
{
|
||||
header: "Aktionen",
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<div>
|
||||
<button
|
||||
className="btn btn-sm btn-error"
|
||||
onClick={async () => {
|
||||
await deleteDispoHistory(row.original.id);
|
||||
dispoTableRef.current?.refresh();
|
||||
}}
|
||||
>
|
||||
löschen
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
},
|
||||
]}
|
||||
] as ColumnDef<ConnectedDispatcher>[]
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
@@ -232,73 +246,73 @@ export const ConnectionHistory: React.FC<{ user: User }> = ({
|
||||
}}
|
||||
prismaModel={"connectedAircraft"}
|
||||
include={{ Station: true }}
|
||||
columns={[
|
||||
{
|
||||
accessorKey: "Station.bosCallsign",
|
||||
header: "Station",
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<Link
|
||||
className="link link-hover"
|
||||
href={`/admin/station/${(row.original as any).id}`}
|
||||
>
|
||||
{(row.original as any).Station.bosCallsign}
|
||||
</Link>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "loginTime",
|
||||
header: "Login",
|
||||
cell: ({ row }) => {
|
||||
return new Date(row.getValue("loginTime")).toLocaleString(
|
||||
"de-DE",
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "Time Online",
|
||||
cell: ({ row }) => {
|
||||
console.log(row.original);
|
||||
const loginTime = new Date(row.original.loginTime).getTime();
|
||||
const logoutTime = new Date(
|
||||
(row.original as any).logoutTime,
|
||||
).getTime();
|
||||
const timeOnline = logoutTime - loginTime;
|
||||
|
||||
const hours = Math.floor(timeOnline / 1000 / 60 / 60);
|
||||
const minutes = Math.floor((timeOnline / 1000 / 60) % 60);
|
||||
|
||||
if ((row.original as any).logoutTime == null) {
|
||||
return <span className="text-success">Online</span>;
|
||||
}
|
||||
|
||||
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 deleteDispoHistory((row.original as any).id);
|
||||
dispoTableRef.current?.refresh();
|
||||
}}
|
||||
columns={
|
||||
[
|
||||
{
|
||||
accessorKey: "Station.bosCallsign",
|
||||
header: "Station",
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<Link
|
||||
className="link link-hover"
|
||||
href={`/admin/station/${row.original.id}`}
|
||||
>
|
||||
löschen
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
{row.original.Station.bosCallsign}
|
||||
</Link>
|
||||
);
|
||||
},
|
||||
},
|
||||
},
|
||||
]}
|
||||
{
|
||||
accessorKey: "loginTime",
|
||||
header: "Login",
|
||||
cell: ({ row }) => {
|
||||
return new Date(row.getValue("loginTime")).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 }>[]
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -44,3 +44,11 @@ export const deleteDispoHistory = async (id: number) => {
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const deletePilotHistory = async (id: number) => {
|
||||
return await prisma.connectedAircraft.delete({
|
||||
where: {
|
||||
id: id,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user