Dispatch Router-Struktur; AddPenalty Layout gefixed
@@ -1,6 +1,6 @@
|
||||
import { Connection } from "./_components/Connection";
|
||||
/* import { ThemeSwap } from "./_components/ThemeSwap"; */
|
||||
import { Audio } from "../../../_components/Audio/Audio";
|
||||
import { Audio } from "../../../../_components/Audio/Audio";
|
||||
/* import { useState } from "react"; */
|
||||
import { ExitIcon, ExternalLinkIcon } from "@radix-ui/react-icons";
|
||||
import Link from "next/link";
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { useDispatchConnectionStore } from "../../../../_store/dispatch/connectionStore";
|
||||
import { useDispatchConnectionStore } from "../../../../../_store/dispatch/connectionStore";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
27
apps/dispatch/app/(app)/dispatch/layout.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import type { Metadata } from "next";
|
||||
import Navbar from "./_components/navbar/Navbar";
|
||||
import { getServerSession } from "api/auth/[...nextauth]/auth";
|
||||
import { Error } from "_components/Error";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "VAR: Disponent",
|
||||
description: "Die neue VAR Leitstelle.",
|
||||
};
|
||||
|
||||
export default async function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
const session = await getServerSession();
|
||||
|
||||
if (!session?.user.permissions.includes("DISPO"))
|
||||
return <Error title="Zugriff verweigert" statusCode={403} />;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
"use client";
|
||||
|
||||
import { Pannel } from "dispatch/_components/pannel/Pannel";
|
||||
import { Pannel } from "(app)/dispatch/_components/pannel/Pannel";
|
||||
import { usePannelStore } from "_store/pannelStore";
|
||||
import { cn } from "@repo/shared-components";
|
||||
import dynamic from "next/dynamic";
|
||||
import { Chat } from "../_components/left/Chat";
|
||||
import { Report } from "../_components/left/Report";
|
||||
import { Chat } from "../../_components/left/Chat";
|
||||
import { Report } from "../../_components/left/Report";
|
||||
import { SituationBoard } from "_components/left/SituationBoard";
|
||||
|
||||
const Map = dynamic(() => import("../_components/map/Map"), { ssr: false });
|
||||
const Map = dynamic(() => import("../../_components/map/Map"), { ssr: false });
|
||||
|
||||
const DispatchPage = () => {
|
||||
const { isOpen } = usePannelStore();
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { Metadata } from "next";
|
||||
import Navbar from "./_components/navbar/Navbar";
|
||||
import { redirect } from "next/navigation";
|
||||
import { getServerSession } from "../api/auth/[...nextauth]/auth";
|
||||
import { getServerSession } from "api/auth/[...nextauth]/auth";
|
||||
import { Error } from "_components/Error";
|
||||
import { prisma } from "@repo/db";
|
||||
|
||||
@@ -27,8 +26,9 @@ export default async function RootLayout({
|
||||
},
|
||||
});
|
||||
|
||||
if (!session || !session.user.firstname) {
|
||||
redirect("/login");
|
||||
if (!session) {
|
||||
console.log(session);
|
||||
return redirect("/logout");
|
||||
}
|
||||
|
||||
if (openPenaltys[0]) {
|
||||
@@ -37,7 +37,7 @@ export default async function RootLayout({
|
||||
<Error
|
||||
title="Du wurdest permanent ausgeschlossen"
|
||||
statusCode={403}
|
||||
description={`Dein Fehlverhalten war so schwerwiegend, dass du dauerhaft von VirtualAirRescue ausgeschlossen wurdest.`}
|
||||
description={`Dein Fehlverhalten war so schwerwiegend, dass du dauerhaft von VirtualAirRescue ausgeschlossen wurdest. Du kannst im Hub weitere Informationen finden.`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -45,7 +45,7 @@ export default async function RootLayout({
|
||||
<Error
|
||||
title="Du hast eine aktive Strafe"
|
||||
statusCode={403}
|
||||
description={`Du bist bis zum ${new Date(openPenaltys[0].until!).toLocaleString()} gesperrt.`}
|
||||
description={`Du bist bis zum ${new Date(openPenaltys[0].until!).toLocaleString()} gesperrt. Du kannst im Hub weitere Informationen finden.`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -53,14 +53,5 @@ export default async function RootLayout({
|
||||
if (!session.user.emailVerified) {
|
||||
return <Error title="E-Mail-Adresse nicht verifiziert" statusCode={403} />;
|
||||
}
|
||||
|
||||
if (!session.user.permissions.includes("PILOT"))
|
||||
return <Error title="Zugriff verweigert" statusCode={403} />;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 626 KiB After Width: | Height: | Size: 626 KiB |
|
Before Width: | Height: | Size: 167 KiB After Width: | Height: | Size: 167 KiB |
|
Before Width: | Height: | Size: 610 KiB After Width: | Height: | Size: 610 KiB |
|
Before Width: | Height: | Size: 194 KiB After Width: | Height: | Size: 194 KiB |
|
Before Width: | Height: | Size: 322 KiB After Width: | Height: | Size: 322 KiB |
@@ -1,7 +1,7 @@
|
||||
import { ConnectedAircraft, Prisma } from "@repo/db";
|
||||
import { usePilotConnectionStore } from "_store/pilot/connectionStore";
|
||||
import { useMrtStore } from "_store/pilot/MrtStore";
|
||||
import { pilotSocket } from "pilot/socket";
|
||||
import { pilotSocket } from "(app)/pilot/socket";
|
||||
import { editConnectedAircraftAPI } from "_querys/aircrafts";
|
||||
import { useEffect } from "react";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
@@ -1,9 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { Connection } from "./_components/Connection";
|
||||
/* import { ThemeSwap } from "./ThemeSwap"; */
|
||||
import { Audio } from "../../../_components/Audio/Audio";
|
||||
/* import { useState } from "react"; */
|
||||
import { Audio } from "_components/Audio/Audio";
|
||||
import { ExitIcon, ExternalLinkIcon } from "@radix-ui/react-icons";
|
||||
import Link from "next/link";
|
||||
import { Settings } from "_components/navbar/Settings";
|
||||
27
apps/dispatch/app/(app)/pilot/layout.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import type { Metadata } from "next";
|
||||
import Navbar from "./_components/navbar/Navbar";
|
||||
import { getServerSession } from "api/auth/[...nextauth]/auth";
|
||||
import { Error } from "_components/Error";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "VAR: Pilot",
|
||||
description: "Die neue VAR Leitstelle.",
|
||||
};
|
||||
|
||||
export default async function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
const session = await getServerSession();
|
||||
|
||||
if (!session?.user.permissions.includes("PILOT"))
|
||||
return <Error title="Zugriff verweigert" statusCode={403} />;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { Mrt } from "pilot/_components/mrt/Mrt";
|
||||
import { Chat } from "../_components/left/Chat";
|
||||
import { Report } from "../_components/left/Report";
|
||||
import { Dme } from "pilot/_components/dme/Dme";
|
||||
import { Mrt } from "(app)/pilot/_components/mrt/Mrt";
|
||||
import { Chat } from "../../_components/left/Chat";
|
||||
import { Report } from "../../_components/left/Report";
|
||||
import { Dme } from "(app)/pilot/_components/dme/Dme";
|
||||
import dynamic from "next/dynamic";
|
||||
import { ConnectedDispatcher } from "tracker/_components/ConnectedDispatcher";
|
||||
|
||||
const Map = dynamic(() => import("../_components/map/Map"), {
|
||||
const Map = dynamic(() => import("_components/map/Map"), {
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
@@ -7,11 +7,11 @@ import { Toaster } from "react-hot-toast";
|
||||
export const Login = () => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const searchParams = useSearchParams();
|
||||
const { data: session } = useSession();
|
||||
const { data: session, status } = useSession();
|
||||
const navigate = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
if (session) {
|
||||
if (status === "authenticated") {
|
||||
navigate.push("/");
|
||||
}
|
||||
}, [session, navigate]);
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
import { toast } from "react-hot-toast";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { ReactNode, useEffect, useState } from "react";
|
||||
import { dispatchSocket } from "dispatch/socket";
|
||||
import { dispatchSocket } from "(app)/dispatch/socket";
|
||||
import { Mission, NotificationPayload } from "@repo/db";
|
||||
import { HPGnotificationToast } from "_components/customToasts/HPGnotification";
|
||||
import { useMapStore } from "_store/mapStore";
|
||||
import { AdminMessageToast } from "_components/customToasts/AdminMessage";
|
||||
import { pilotSocket } from "pilot/socket";
|
||||
import { pilotSocket } from "(app)/pilot/socket";
|
||||
|
||||
export function QueryProvider({ children }: { children: ReactNode }) {
|
||||
const mapStore = useMapStore((s) => s);
|
||||
|
||||
@@ -150,21 +150,29 @@ export default function AdminPanel() {
|
||||
</td>
|
||||
<td className="flex gap-2">
|
||||
<PenaltyDropdown
|
||||
btnName="Verbindung trennen"
|
||||
btnClassName="btn-warning"
|
||||
btnTip="Kick"
|
||||
btnTip="Die Verbindung zur Leitstelle wird für diesesn Nutzer unterbrochen"
|
||||
Icon={<RedoDot size={15} />}
|
||||
onClick={({ reason }) =>
|
||||
kickPilotMutation.mutate({ id: p.id, reason })
|
||||
}
|
||||
/>
|
||||
<PenaltyDropdown
|
||||
btnName="Kick + Berechtigungen entfernen"
|
||||
btnClassName="btn-error tooltip-error"
|
||||
btnTip="Kick + Berechtigungen entfernen"
|
||||
btnTip="Dadurch wird sich der Pilot nicht mehr mit dem VAR verbinden können."
|
||||
showDatePicker
|
||||
Icon={<LockKeyhole size={15} />}
|
||||
onClick={({ reason, until }) =>
|
||||
kickPilotMutation.mutate({ id: p.id, reason, bann: true, until })
|
||||
onClick={({ reason, until }) => {
|
||||
if (!until) {
|
||||
toast.error(
|
||||
"Bitte wähle ein Datum aus. Ein permanenter Bann ist nur vom HUB aus möglich.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
kickPilotMutation.mutate({ id: p.id, reason, bann: true, until });
|
||||
}}
|
||||
/>
|
||||
<a
|
||||
href={`${process.env.NEXT_PUBLIC_HUB_URL}/admin/user/${p.userId}`}
|
||||
@@ -203,16 +211,18 @@ export default function AdminPanel() {
|
||||
</td>
|
||||
<td className="flex gap-2">
|
||||
<PenaltyDropdown
|
||||
btnName="Verbindung trennen"
|
||||
btnClassName="btn-warning"
|
||||
btnTip="Kick"
|
||||
btnTip="Die Verbindung zur Leitstelle wird für diesesn Nutzer unterbrochen"
|
||||
Icon={<RedoDot size={15} />}
|
||||
onClick={({ reason }) =>
|
||||
kickDispatchMutation.mutate({ id: d.id, reason })
|
||||
}
|
||||
/>
|
||||
<PenaltyDropdown
|
||||
btnName="Kick + Berechtigungen entfernen"
|
||||
btnClassName="btn-error tooltip-error"
|
||||
btnTip="Kick + Berechtigungen entfernen"
|
||||
btnTip="Dadurch wird sich der Pilot nicht mehr mit dem VAR verbinden können."
|
||||
showDatePicker
|
||||
Icon={<LockKeyhole size={15} />}
|
||||
onClick={({ reason, until }) =>
|
||||
@@ -264,12 +274,6 @@ export default function AdminPanel() {
|
||||
>
|
||||
<RedoDot size={15} />
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-xs btn-square btn-error btn-soft tooltip tooltip-bottom tooltip-error"
|
||||
data-tip="Kick + Berechtigungen entfernen"
|
||||
>
|
||||
<LockKeyhole size={15} />
|
||||
</button>
|
||||
<a
|
||||
href={`${process.env.NEXT_PUBLIC_HUB_URL}/admin/user/${p.participant.attributes.userId}`}
|
||||
target="_blank"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { dispatchSocket } from "dispatch/socket";
|
||||
import { dispatchSocket } from "(app)/dispatch/socket";
|
||||
import {
|
||||
handleDisconnect,
|
||||
handleLocalTrackUnpublished,
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
handleTrackUnsubscribed,
|
||||
} from "_helpers/liveKitEventHandler";
|
||||
import { ConnectionQuality, Participant, Room, RoomEvent, RpcInvocationData } from "livekit-client";
|
||||
import { pilotSocket } from "pilot/socket";
|
||||
import { pilotSocket } from "(app)/pilot/socket";
|
||||
import { create } from "zustand";
|
||||
import axios from "axios";
|
||||
import { useDispatchConnectionStore } from "_store/dispatch/connectionStore";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { create } from "zustand";
|
||||
import { dispatchSocket } from "../../dispatch/socket";
|
||||
import { dispatchSocket } from "../../(app)/dispatch/socket";
|
||||
import { useAudioStore } from "_store/audioStore";
|
||||
import { ConnectedDispatcher } from "@repo/db";
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { create } from "zustand";
|
||||
import { ChatMessage } from "@repo/db";
|
||||
import { dispatchSocket } from "dispatch/socket";
|
||||
import { pilotSocket } from "pilot/socket";
|
||||
import { dispatchSocket } from "(app)/dispatch/socket";
|
||||
import { pilotSocket } from "(app)/pilot/socket";
|
||||
|
||||
interface ChatStore {
|
||||
situationTabOpen: boolean;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { MissionSdsLog, Station } from "@repo/db";
|
||||
import { fmsStatusDescription } from "_data/fmsStatusDescription";
|
||||
import { DisplayLineProps } from "pilot/_components/mrt/Mrt";
|
||||
import { DisplayLineProps } from "(app)/pilot/_components/mrt/Mrt";
|
||||
import { create } from "zustand";
|
||||
import { syncTabs } from "zustand-sync-tabs";
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { create } from "zustand";
|
||||
import { dispatchSocket } from "../../dispatch/socket";
|
||||
import { dispatchSocket } from "../../(app)/dispatch/socket";
|
||||
import { ConnectedAircraft, Mission, MissionSdsLog, Station, User } from "@repo/db";
|
||||
import { pilotSocket } from "pilot/socket";
|
||||
import { pilotSocket } from "(app)/pilot/socket";
|
||||
import { useDmeStore } from "_store/pilot/dmeStore";
|
||||
import { useMrtStore } from "_store/pilot/MrtStore";
|
||||
import { useAudioStore } from "_store/audioStore";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Mission, Station, User } from "@repo/db";
|
||||
import { DisplayLineProps } from "pilot/_components/dme/Dme";
|
||||
import { DisplayLineProps } from "(app)/pilot/_components/dme/Dme";
|
||||
import { create } from "zustand";
|
||||
import { syncTabs } from "zustand-sync-tabs";
|
||||
|
||||
|
||||
@@ -76,15 +76,7 @@ export const options: AuthOptions = {
|
||||
},
|
||||
});
|
||||
if (!dbUser) {
|
||||
return {
|
||||
...session,
|
||||
user: {
|
||||
name: null,
|
||||
email: null,
|
||||
image: null,
|
||||
},
|
||||
expires: new Date().toISOString(),
|
||||
};
|
||||
return null as any;
|
||||
}
|
||||
return {
|
||||
...session,
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
import type { Metadata } from "next";
|
||||
import Navbar from "./_components/navbar/Navbar";
|
||||
import { redirect } from "next/navigation";
|
||||
import { getServerSession } from "../api/auth/[...nextauth]/auth";
|
||||
import { Error } from "_components/Error";
|
||||
import { prisma } from "@repo/db";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "VAR: Disponent",
|
||||
description: "Die neue VAR Leitstelle.",
|
||||
};
|
||||
|
||||
export default async function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
const session = await getServerSession();
|
||||
const openPenaltys = await prisma.penalty.findMany({
|
||||
where: {
|
||||
userId: session?.user.id,
|
||||
until: {
|
||||
gte: new Date(),
|
||||
},
|
||||
suspended: false,
|
||||
|
||||
type: { in: ["TIME_BAN", "BAN"] },
|
||||
},
|
||||
});
|
||||
|
||||
if (!session || !session.user) {
|
||||
redirect("/login");
|
||||
}
|
||||
|
||||
if (openPenaltys[0]) {
|
||||
if (openPenaltys[0].type === "BAN") {
|
||||
return (
|
||||
<Error
|
||||
title="Du wurdest permanent ausgeschlossen"
|
||||
statusCode={403}
|
||||
description={`Dein Fehlverhalten war so schwerwiegend, dass du dauerhaft von VirtualAirRescue ausgeschlossen wurdest.`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Error
|
||||
title="Du hast eine aktive Strafe"
|
||||
statusCode={403}
|
||||
description={`Du bist bis zum ${new Date(openPenaltys[0].until!).toLocaleString()} gesperrt.`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (!session.user.emailVerified)
|
||||
return <Error title="E-Mail-Adresse nicht verifiziert" statusCode={403} />;
|
||||
|
||||
if (!session.user.permissions.includes("DISPO"))
|
||||
return <Error title="Zugriff verweigert" statusCode={403} />;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -42,8 +42,9 @@ export default async function RootLayout({
|
||||
style: {
|
||||
background: "var(--color-base-100)",
|
||||
color: "var(--color-base-content)",
|
||||
zIndex: 9999,
|
||||
},
|
||||
duration: 4000,
|
||||
duration: 5000,
|
||||
}}
|
||||
position="top-left"
|
||||
reverseOrder={false}
|
||||
|
||||
@@ -8,7 +8,7 @@ export default () => {
|
||||
const session = useSession();
|
||||
|
||||
useEffect(() => {
|
||||
if (session.status !== "authenticated") {
|
||||
if (session.status === "unauthenticated") {
|
||||
router.replace("/login");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
import ModeSwitchDropdown from "_components/navbar/ModeSwitchDropdown";
|
||||
import { useSession } from "next-auth/react";
|
||||
import dynamic from "next/dynamic";
|
||||
import { ConnectedDispatcher } from "tracker/_components/ConnectedDispatcher";
|
||||
import { ConnectedDispatcher } from "./_components/ConnectedDispatcher";
|
||||
|
||||
const Map = dynamic(() => import("../_components/map/Map"), {
|
||||
const Map = dynamic(() => import("_components/map/Map"), {
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ export const FirstPath = () => {
|
||||
}
|
||||
}}
|
||||
>
|
||||
{page === "path" ? "Weiter" : "Pfad auswählen"}
|
||||
{page === "path" ? "Weiter" : "Intro abschließen"}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -256,7 +256,7 @@ export const DispoStats = async () => {
|
||||
</div>
|
||||
<div className="stat-title">Disponent Login Zeit</div>
|
||||
<div className="stat-value text-secondary">
|
||||
{hours}h {minutes}m
|
||||
{hours}h {minutes}min
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -6,10 +6,7 @@ import {
|
||||
ConnectedAircraft,
|
||||
ConnectedDispatcher,
|
||||
DiscordAccount,
|
||||
Penalty,
|
||||
PenaltyType,
|
||||
PERMISSION,
|
||||
Report,
|
||||
Station,
|
||||
User,
|
||||
} from "@repo/db";
|
||||
@@ -57,7 +54,7 @@ import { Error } from "_components/Error";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { setStandardName } from "../../../../../../helper/discord";
|
||||
import { penaltyColumns } from "(app)/admin/penalty/columns";
|
||||
import { addPenalty, editPenalty, editPenaltys } from "(app)/admin/penalty/actions";
|
||||
import { addPenalty, editPenaltys } from "(app)/admin/penalty/actions";
|
||||
import { reportColumns } from "(app)/admin/report/columns";
|
||||
|
||||
interface ProfileFormProps {
|
||||
@@ -123,6 +120,8 @@ export const ProfileForm: React.FC<ProfileFormProps> = ({ user }: ProfileFormPro
|
||||
{form.formState.errors.lastname && (
|
||||
<p className="text-error">{form.formState.errors.lastname?.message}</p>
|
||||
)}
|
||||
{session.data?.user.permissions.includes("ADMIN_USER_ADVANCED") && (
|
||||
<>
|
||||
<label className="floating-label w-full">
|
||||
<span className="text-lg flex items-center gap-2">
|
||||
<EnvelopeClosedIcon /> E-Mail
|
||||
@@ -143,6 +142,8 @@ export const ProfileForm: React.FC<ProfileFormProps> = ({ user }: ProfileFormPro
|
||||
<input type="checkbox" {...form.register("emailVerified")} className="checkbox" />
|
||||
Email bestätigt
|
||||
</label>
|
||||
</>
|
||||
)}
|
||||
<Select
|
||||
isMulti
|
||||
form={form}
|
||||
@@ -334,7 +335,8 @@ export const UserPenalties = ({ user }: { user: User }) => {
|
||||
</span>
|
||||
<div className="flex gap-2">
|
||||
<PenaltyDropdown
|
||||
Icon={<RedoDot size={15} />}
|
||||
btnName="Zeitstrafe hinzufügen"
|
||||
Icon={<Timer size={15} />}
|
||||
onClick={async ({ reason, until }) => {
|
||||
if (!reason) return toast.error("Bitte gib einen Grund für die Strafe an.");
|
||||
if (!until) return toast.error("Bitte gib eine Dauer für die Strafe ein.");
|
||||
@@ -350,12 +352,13 @@ export const UserPenalties = ({ user }: { user: User }) => {
|
||||
penaltyTable.current?.refresh();
|
||||
toast.success("Time-Ban wurde hinzugefügt!");
|
||||
}}
|
||||
btnClassName="btn btn-outline btn-warning tooltip-warning"
|
||||
btnTip="Timeban hinzufügen"
|
||||
btnClassName="btn-outline btn-warning tooltip-warning"
|
||||
btnTip="Der Nutzer wird für eine bestimmte Zeit gesperrt"
|
||||
showDatePicker={true}
|
||||
/>
|
||||
{session.data?.user.permissions.includes("ADMIN_USER_ADVANCED") && (
|
||||
<PenaltyDropdown
|
||||
btnName="Bannen"
|
||||
Icon={<LockKeyhole size={15} />}
|
||||
onClick={async ({ reason }) => {
|
||||
if (!reason) return toast.error("Bitte gib einen Grund für die Strafe an.");
|
||||
@@ -371,7 +374,7 @@ export const UserPenalties = ({ user }: { user: User }) => {
|
||||
penaltyTable.current?.refresh();
|
||||
toast.success("Ban wurde hinzugefügt!");
|
||||
}}
|
||||
btnClassName="btn btn-outline btn-error tooltip-error"
|
||||
btnClassName="btn-outline btn-error tooltip-error"
|
||||
btnTip="Nutzerkonto sperren"
|
||||
/>
|
||||
)}
|
||||
@@ -503,7 +506,7 @@ export const AdminForm = ({
|
||||
role="submit"
|
||||
className="btn-sm flex-1 min-w-[250px] btn-outline btn-warning"
|
||||
>
|
||||
<HobbyKnifeIcon /> HUB zugang entsperren
|
||||
<HobbyKnifeIcon /> Account entsperren
|
||||
</Button>
|
||||
)}
|
||||
{discordAccount && (
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { User2 } from "lucide-react";
|
||||
import { PaginatedTable } from "../../../_components/PaginatedTable";
|
||||
import { getServerSession } from "api/auth/[...nextauth]/auth";
|
||||
|
||||
const AdminUserPage = async () => {
|
||||
const session = await getServerSession();
|
||||
return (
|
||||
<>
|
||||
<PaginatedTable
|
||||
@@ -27,10 +29,14 @@ const AdminUserPage = async () => {
|
||||
header: "Nachname",
|
||||
accessorKey: "lastname",
|
||||
},
|
||||
...(session?.user.permissions.includes("ADMIN_USER_ADVANCED")
|
||||
? [
|
||||
{
|
||||
header: "Email",
|
||||
accessorKey: "email",
|
||||
},
|
||||
]
|
||||
: []),
|
||||
]}
|
||||
leftOfSearch={
|
||||
<p className="text-2xl font-semibold text-left flex items-center gap-2">
|
||||
|
||||
@@ -57,7 +57,7 @@ export const Penalty = async () => {
|
||||
<div className="card-body text-base-300">
|
||||
<h2 className="card-title text-3xl">
|
||||
<TriangleAlert />
|
||||
Du wurdest permanent von VirtualAirRescue ausgeschlossen.
|
||||
Du wurdest permanent von Virtual Air Rescue ausgeschlossen.
|
||||
</h2>
|
||||
<p className="text-left font-bold">
|
||||
Dein Fehlverhalten war so schwerwiegend, dass du dauerhaft von VirtualAirRescue
|
||||
|
||||
@@ -7,11 +7,13 @@ export const PenaltyDropdown = ({
|
||||
btnClassName,
|
||||
showDatePicker,
|
||||
btnTip,
|
||||
btnName,
|
||||
Icon,
|
||||
}: {
|
||||
onClick: (data: { reason: string; until: Date | null }) => void;
|
||||
showDatePicker?: boolean;
|
||||
btnClassName?: string;
|
||||
btnName: string;
|
||||
btnTip?: string;
|
||||
Icon: ReactNode;
|
||||
}) => {
|
||||
@@ -20,22 +22,26 @@ export const PenaltyDropdown = ({
|
||||
return (
|
||||
<details className="dropdown dropdown-left dropdown-center">
|
||||
<summary className={cn("btn btn-xs btn-square btn-soft", btnClassName)}>{Icon}</summary>
|
||||
<div className="dropdown-content flex gap-3 items-center bg-base-100 rounded-box z-1 p-2 mr-3 shadow-sm">
|
||||
<input
|
||||
<div
|
||||
className="dropdown-content bg-base-100 rounded-box z-1 p-4 shadow-sm space-y-4 shadow-md"
|
||||
style={{ minWidth: "500px", right: "40px" }}
|
||||
>
|
||||
<h2 className="text-xl font-blod text-center">{btnName}</h2>
|
||||
<textarea
|
||||
value={reason}
|
||||
onChange={(e) => setReason(e.target.value)}
|
||||
type="text"
|
||||
className="input min-w-[250px]"
|
||||
className="input w-full"
|
||||
placeholder="Begründung"
|
||||
style={{ minHeight: "100px" }}
|
||||
/>
|
||||
{showDatePicker && (
|
||||
<select
|
||||
className="select min-w-[150px] select-bordered"
|
||||
className="select w-full select-bordered"
|
||||
value={until}
|
||||
onChange={(e) => setUntil(e.target.value)}
|
||||
>
|
||||
<option value="default" disabled>
|
||||
Unbegrenzt
|
||||
Keine
|
||||
</option>
|
||||
<option value="1h">1 Stunde</option>
|
||||
<option value="6h">6 Stunden</option>
|
||||
@@ -51,7 +57,7 @@ export const PenaltyDropdown = ({
|
||||
</select>
|
||||
)}
|
||||
<button
|
||||
className={cn("btn btn-square btn-soft tooltip tooltip-bottom", btnClassName)}
|
||||
className={cn("btn w-full btn-square btn-soft tooltip tooltip-bottom", btnClassName)}
|
||||
data-tip={btnTip}
|
||||
onClick={() => {
|
||||
let untilDate: Date | null = null;
|
||||
@@ -98,7 +104,7 @@ export const PenaltyDropdown = ({
|
||||
onClick({ reason, until: untilDate });
|
||||
}}
|
||||
>
|
||||
{Icon}
|
||||
{Icon} {btnName}
|
||||
</button>
|
||||
</div>
|
||||
</details>
|
||||
|
||||