This commit is contained in:
PxlLoewe
2025-07-29 15:52:34 -07:00
parent 627060e32e
commit 99c3024d85
2 changed files with 59 additions and 13 deletions

View File

@@ -6,6 +6,7 @@ import {
ConnectedAircraft, ConnectedAircraft,
ConnectedDispatcher, ConnectedDispatcher,
DiscordAccount, DiscordAccount,
Penalty,
PERMISSION, PERMISSION,
Station, Station,
User, User,
@@ -46,6 +47,7 @@ import {
ShieldUser, ShieldUser,
Timer, Timer,
Trash2, Trash2,
TriangleAlert,
Users, Users,
} from "lucide-react"; } from "lucide-react";
import Link from "next/link"; import Link from "next/link";
@@ -255,6 +257,7 @@ export const ProfileForm: React.FC<ProfileFormProps> = ({ user }: ProfileFormPro
export const ConnectionHistory: React.FC<{ user: User }> = ({ user }: { user: User }) => { export const ConnectionHistory: React.FC<{ user: User }> = ({ user }: { user: User }) => {
const dispoTableRef = useRef<PaginatedTableRef>(null); const dispoTableRef = useRef<PaginatedTableRef>(null);
const pilotTableRef = useRef<PaginatedTableRef>(null);
return ( return (
<div className="card-body flex-row flex-wrap"> <div className="card-body flex-row flex-wrap">
<div className="flex-1"> <div className="flex-1">
@@ -302,7 +305,7 @@ export const ConnectionHistory: React.FC<{ user: User }> = ({ user }: { user: Us
cell: ({ row }) => { cell: ({ row }) => {
return ( return (
<div> <div>
<button <Button
className="btn btn-sm btn-error" className="btn btn-sm btn-error"
onClick={async () => { onClick={async () => {
await deleteDispoHistory(row.original.id); await deleteDispoHistory(row.original.id);
@@ -310,7 +313,7 @@ export const ConnectionHistory: React.FC<{ user: User }> = ({ user }: { user: Us
}} }}
> >
löschen löschen
</button> </Button>
</div> </div>
); );
}, },
@@ -324,7 +327,7 @@ export const ConnectionHistory: React.FC<{ user: User }> = ({ user }: { user: Us
<PlaneIcon className="h-5 w-5" /> Pilot-Verbindungs Historie <PlaneIcon className="h-5 w-5" /> Pilot-Verbindungs Historie
</h2> </h2>
<PaginatedTable <PaginatedTable
ref={dispoTableRef} ref={pilotTableRef}
filter={{ filter={{
userId: user.id, userId: user.id,
}} }}
@@ -375,15 +378,15 @@ export const ConnectionHistory: React.FC<{ user: User }> = ({ user }: { user: Us
cell: ({ row }) => { cell: ({ row }) => {
return ( return (
<div> <div>
<button <Button
className="btn btn-sm btn-error" className="btn btn-sm btn-error"
onClick={async () => { onClick={async () => {
await deletePilotHistory(row.original.id); await deletePilotHistory(row.original.id);
dispoTableRef.current?.refresh(); pilotTableRef.current?.refresh();
}} }}
> >
löschen löschen
</button> </Button>
</div> </div>
); );
}, },
@@ -408,6 +411,7 @@ export const UserPenalties = ({ user }: { user: User }) => {
</span> </span>
<div className="flex gap-2"> <div className="flex gap-2">
<PenaltyDropdown <PenaltyDropdown
showBtnName
btnName="Zeitstrafe hinzufügen" btnName="Zeitstrafe hinzufügen"
Icon={<Timer size={15} />} Icon={<Timer size={15} />}
onClick={async ({ reason, until }) => { onClick={async ({ reason, until }) => {
@@ -437,6 +441,7 @@ export const UserPenalties = ({ user }: { user: User }) => {
/> />
{session.data?.user.permissions.includes("ADMIN_USER_ADVANCED") && ( {session.data?.user.permissions.includes("ADMIN_USER_ADVANCED") && (
<PenaltyDropdown <PenaltyDropdown
showBtnName
btnName="Bannen" btnName="Bannen"
Icon={<LockKeyhole size={15} />} Icon={<LockKeyhole size={15} />}
onClick={async ({ reason }) => { onClick={async ({ reason }) => {
@@ -528,6 +533,12 @@ interface AdminFormProps {
open: number; open: number;
total60Days: number; total60Days: number;
}; };
openBans: (Penalty & {
CreatedUser: User | null;
})[];
openTimebans: (Penalty & {
CreatedUser: User | null;
})[];
} }
export const AdminForm = ({ export const AdminForm = ({
@@ -536,6 +547,8 @@ export const AdminForm = ({
pilotTime, pilotTime,
reports, reports,
discordAccount, discordAccount,
openBans,
openTimebans,
}: AdminFormProps) => { }: AdminFormProps) => {
const router = useRouter(); const router = useRouter();
const { data: session } = useSession(); const { data: session } = useSession();
@@ -627,6 +640,33 @@ export const AdminForm = ({
)} )}
</div> </div>
</div> </div>
{(!!openBans.length || !!openTimebans.length) && (
<div role="alert" className="alert alert-warning alert-outline flex flex-col">
<div className="flex items-center gap-2">
<TriangleAlert />
{openBans.map((ban) => (
<div key={ban.id}>
<h3 className="text-lg font-semibold">Permanent ausgeschlossen</h3>
{ban.reason} (von {ban.CreatedUser?.firstname} {ban.CreatedUser?.lastname} -{" "}
{ban.CreatedUser?.publicId})
</div>
))}
{openTimebans.map((timeban) => (
<div key={timeban.id}>
<h3 className="text-lg font-semibold">
Zeitstrafe bis{" "}
{timeban.until ? new Date(timeban.until).toLocaleString("de-DE") : "unbekannt"}
</h3>
{timeban.reason} ({timeban.CreatedUser?.firstname} {timeban.CreatedUser?.lastname} -{" "}
{timeban.CreatedUser?.publicId})
</div>
))}
</div>
<p className="text-sm text-gray-400">
Achtung! Die Strafe(n) sind aktiv, die Rechte des Nutzers müssen nicht angepasst werden!
</p>
</div>
)}
<h2 className="card-title"> <h2 className="card-title">
<ChartBarBigIcon className="h-5 w-5" /> Aktivität <ChartBarBigIcon className="h-5 w-5" /> Aktivität
</h2> </h2>

View File

@@ -9,12 +9,14 @@ export const PenaltyDropdown = ({
btnTip, btnTip,
btnName, btnName,
Icon, Icon,
showBtnName = false,
}: { }: {
onClick: (data: { reason: string; until: Date | null }) => void; onClick: (data: { reason: string; until: Date | null }) => void;
showDatePicker?: boolean; showDatePicker?: boolean;
btnClassName?: string; btnClassName?: string;
btnName: string; btnName: string;
btnTip?: string; btnTip?: string;
showBtnName?: boolean;
Icon: ReactNode; Icon: ReactNode;
}) => { }) => {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
@@ -25,25 +27,29 @@ export const PenaltyDropdown = ({
<div tabIndex={0} role="button"></div> <div tabIndex={0} role="button"></div>
<div className="indicator"> <div className="indicator">
<button <button
className={cn("btn btn-xs btn-square btn-soft cursor-pointer", btnClassName)} className={cn(
"btn btn-xs btn-soft cursor-pointer",
!showBtnName && "btn-square",
btnClassName,
)}
onClick={() => setOpen(!open)} onClick={() => setOpen(!open)}
> >
{Icon} {Icon} {showBtnName && <span className="hidden md:inline-block">{btnName}</span>}
</button> </button>
</div> </div>
{open && ( {open && (
<div <div
className="dropdown-content bg-base-100 rounded-box z-1 p-4 shadow-sm space-y-4 shadow-md" className="dropdown-content bg-base-100 rounded-box z-1 space-y-4 p-4 shadow-md shadow-sm"
style={{ minWidth: "500px", right: "40px" }} style={{ minWidth: "500px", right: "40px" }}
> >
<button <button
className="absolute top-2 right-2 btn btn-xs btn-circle btn-ghost" className="btn btn-xs btn-circle btn-ghost absolute right-2 top-2"
onClick={() => setOpen(false)} onClick={() => setOpen(false)}
type="button" type="button"
> >
<span className="text-xl leading-none">&times;</span> <span className="text-xl leading-none">&times;</span>
</button> </button>
<h2 className="text-xl font-bold text-center">{btnName}</h2> <h2 className="text-center text-xl font-bold">{btnName}</h2>
<textarea <textarea
value={reason} value={reason}
onChange={(e) => setReason(e.target.value)} onChange={(e) => setReason(e.target.value)}
@@ -53,7 +59,7 @@ export const PenaltyDropdown = ({
/> />
{showDatePicker && ( {showDatePicker && (
<select <select
className="select w-full select-bordered" className="select select-bordered w-full"
value={until} value={until}
onChange={(e) => setUntil(e.target.value)} onChange={(e) => setUntil(e.target.value)}
> >
@@ -74,7 +80,7 @@ export const PenaltyDropdown = ({
</select> </select>
)} )}
<button <button
className={cn("btn w-full btn-square btn-soft tooltip tooltip-bottom", btnClassName)} className={cn("btn btn-square btn-soft tooltip tooltip-bottom w-full", btnClassName)}
data-tip={btnTip} data-tip={btnTip}
onClick={() => { onClick={() => {
let untilDate: Date | null = null; let untilDate: Date | null = null;