Einstellungen sind eingeschränkt wenn Nutzer gebannt ist

This commit is contained in:
PxlLoewe
2025-06-25 18:07:05 -07:00
parent 577a18d595
commit b95319d61e
6 changed files with 191 additions and 68 deletions

View File

@@ -1,6 +1,6 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
import { DiscordAccount, User } from "@repo/db";
import { DiscordAccount, Penalty, User } from "@repo/db";
import { useState } from "react";
import { useForm } from "react-hook-form";
import { z } from "zod";
@@ -21,11 +21,19 @@ import {
} from "@radix-ui/react-icons";
import toast from "react-hot-toast";
import { UserOptionalDefaults, UserOptionalDefaultsSchema } from "@repo/db/zod";
import { Bell, Plane } from "lucide-react";
import { Bell, CircleAlert, Plane, Trash2 } from "lucide-react";
import Link from "next/link";
import { sendVerificationLink } from "(app)/admin/user/action";
import { deleteUser, sendVerificationLink } from "(app)/admin/user/action";
export const ProfileForm = ({
user,
penaltys,
}: {
user: User;
penaltys: Penalty[];
}): React.JSX.Element => {
const canEdit = penaltys.length === 0 && user.isBanned;
export const ProfileForm = ({ user }: { user: User }): React.JSX.Element => {
const schema = z.object({
firstname: z.string().min(2).max(30),
lastname: z.string().min(2).max(30),
@@ -59,7 +67,6 @@ export const ProfileForm = ({ user }: { user: User }): React.JSX.Element => {
},
resolver: zodResolver(schema),
});
console.log(form.formState.errors);
return (
<form
className="card-body"
@@ -70,7 +77,7 @@ export const ProfileForm = ({ user }: { user: User }): React.JSX.Element => {
if (user.email !== values.email) {
await sendVerificationLink(user.id);
toast.success(
"Deine E-Mail Addresse hat sich geändert, wir haben die einen Link gesendet!",
"Deine E-Mail Addresse hat sich geändert, wir haben dir einen Link gesendet!",
);
} else {
toast.success("Deine Änderungen wurden gespeichert!", {
@@ -87,60 +94,81 @@ export const ProfileForm = ({ user }: { user: User }): React.JSX.Element => {
<MixerHorizontalIcon className="w-5 h-5" /> Persönliche Informationen
</h2>
<div className="text-left">
<label className="floating-label w-full mb-5 mt-5">
<span className="text-lg flex items-center gap-2">
<PersonIcon /> Vorname
</span>
<input
{...form.register("firstname")}
type="text"
className="input input-bordered w-full"
defaultValue={user.firstname}
placeholder="Vorname"
/>
</label>
{form.formState.errors.firstname && (
<p className="text-error">{form.formState.errors.firstname.message}</p>
{!canEdit && (
<div className="text-left">
<h2 className="text-lg text-warning card-title">
Du kannst deine Stammdaten nicht bearbeiten!
</h2>
<p className="text-sm text-gray-400">
Scheinbar hast du aktuell aktive Strafen oder dein Account ist gesperrt. Um unsere
Community zu schützen, kannst du deine persönlichen Informationen erst bearbeiten,
wenn keine Strafen mehr aktiv sind und dein Account nicht gesperrt ist.
</p>
</div>
)}
<label className="floating-label w-full mb-5">
<span className="text-lg flex items-center gap-2">
<PersonIcon /> Nachname
</span>
<input
{...form.register("lastname")}
type="text"
className="input input-bordered w-full"
defaultValue={user.lastname}
placeholder="Nachname"
/>
</label>
{form.formState.errors.lastname && (
<p className="text-error">{form.formState.errors.lastname?.message}</p>
)}
<label className="label">
<input type="checkbox" {...form.register("settingsHideLastname")} className="checkbox" />
Initialien des Nachnamens verstecken
</label>
<label className="floating-label w-full mt-4">
<span className="text-lg flex items-center gap-2">
<EnvelopeClosedIcon /> E-Mail
</span>
<input
value={form.watch("email")}
type="text"
className="input input-bordered w-full"
onChange={(e) => {
form.setValue("email", e.target.value.trim(), {
shouldDirty: true,
});
form.setValue("emailVerified", false);
}}
placeholder="E-Mail"
/>
</label>
{form.formState.errors.email && (
<p className="text-error">{form.formState.errors.email?.message}</p>
{canEdit && (
<>
<label className="floating-label w-full mb-5 mt-5">
<span className="text-lg flex items-center gap-2">
<PersonIcon /> Vorname
</span>
<input
{...form.register("firstname")}
type="text"
defaultValue={user.firstname}
placeholder="Vorname"
className="input input-bordered w-full"
/>
</label>
{form.formState.errors.firstname && (
<p className="text-error">{form.formState.errors.firstname.message}</p>
)}
<label className="floating-label w-full mb-5">
<span className="text-lg flex items-center gap-2">
<PersonIcon /> Nachname
</span>
<input
{...form.register("lastname")}
type="text"
defaultValue={user.lastname}
placeholder="Nachname"
className="input input-bordered w-full"
/>
</label>
{form.formState.errors.lastname && (
<p className="text-error">{form.formState.errors.lastname?.message}</p>
)}
<label className="label">
<input
type="checkbox"
{...form.register("settingsHideLastname")}
className="checkbox"
/>
Initialien des Nachnamens verstecken
</label>
<label className="floating-label w-full mt-4">
<span className="text-lg flex items-center gap-2">
<EnvelopeClosedIcon /> E-Mail
</span>
<input
value={form.watch("email")}
type="text"
onChange={(e) => {
form.setValue("email", e.target.value.trim(), {
shouldDirty: true,
});
form.setValue("emailVerified", false);
}}
placeholder="E-Mail"
className="input input-bordered w-full"
/>
</label>
{form.formState.errors.email && (
<p className="text-error">{form.formState.errors.email?.message}</p>
)}
</>
)}
<div className="card-actions justify-center pt-6">
<Button
role="submit"
@@ -240,7 +268,6 @@ export const SocialForm = ({
<input
type="number"
className="input input-bordered w-full"
placeholder="1445241"
defaultValue={user.vatsimCid as number | undefined}
{...form.register("vatsimCid", {
valueAsNumber: true,
@@ -265,6 +292,43 @@ export const SocialForm = ({
);
};
export const DeleteForm = ({ user, penaltys }: { user: User; penaltys: Penalty[] }) => {
const router = useRouter();
const userCanDelete = penaltys.length === 0 && !user.isBanned;
return (
<div className="card-body">
<h2 className="card-title mb-5">
<CircleAlert className="w-5 h-5" /> Danger-Zone
</h2>
{!userCanDelete && (
<div className="text-left">
<h2 className="text-lg text-warning">Du kannst dein Konto zurzeit nicht löschen!</h2>
<p className="text-sm text-gray-400 ">
Scheinbar hast du aktuell zurzeit aktive Strafen. Um unsere Community zu schützen kannst
du einen Account erst löschen wenn deine Strafe nicht mehr aktiv ist
</p>
</div>
)}
{userCanDelete && (
<div
className="tooltip flex-1 min-w-[250px] tooltip-warning"
data-tip="Achtung! Dies löscht deinen Account und alle zugehörigen Daten. Dieser Vorgang ist nicht rückgängig zu machen."
>
<Button
className="btn-error btn-outline btn-sm w-full"
onClick={async () => {
await deleteUser(user.id);
router.push("/login");
}}
>
<Trash2 size={15} /> Konto sofort löschen
</Button>
</div>
)}
</div>
);
};
export const PasswordForm = (): React.JSX.Element => {
const schema = z.object({
password: z.string().min(2).max(30),

View File

@@ -1,20 +1,44 @@
import { prisma } from "@repo/db";
import { getServerSession } from "../../api/auth/[...nextauth]/auth";
import { ProfileForm, SocialForm, PasswordForm, PilotForm } from "./_components/forms";
import { ProfileForm, SocialForm, PasswordForm, PilotForm, DeleteForm } from "./_components/forms";
import { GearIcon } from "@radix-ui/react-icons";
import { Error } from "_components/Error";
export default async function Page() {
const session = await getServerSession();
if (!session) return null;
if (!session)
return <Error statusCode={401} title="Du musst angemeldet sein, um diese Seite zu sehen." />;
const user = await prisma.user.findFirst({
where: {
id: session.user.id,
Penaltys: {
some: {
until: {
gte: new Date(),
},
suspended: false,
},
},
},
include: {
discordAccounts: true,
Penaltys: true,
},
});
if (!user) return null;
const userPenaltys = await prisma.penalty.findMany({
where: {
userId: session.user.id,
until: {
gte: new Date(),
},
type: {
in: ["TIME_BAN", "BAN"],
},
suspended: false,
},
});
if (!user) return <Error statusCode={401} title="Dein Account wurde nicht gefunden" />;
const discordAccount = user?.discordAccounts[0];
return (
<div className="grid grid-cols-6 gap-4">
@@ -24,7 +48,7 @@ export default async function Page() {
</p>
</div>
<div className="card bg-base-200 shadow-xl mb-4 col-span-6 xl:col-span-3">
<ProfileForm user={user} />
<ProfileForm user={user} penaltys={userPenaltys} />
</div>
<div className="card bg-base-200 shadow-xl mb-4 col-span-6 xl:col-span-3">
<SocialForm discordAccount={discordAccount} user={user} />
@@ -35,6 +59,9 @@ export default async function Page() {
<div className="card bg-base-200 shadow-xl mb-4 col-span-6 xl:col-span-3">
<PilotForm user={user} />
</div>
<div className="card bg-base-200 shadow-xl mb-4 col-span-6 xl:col-span-3">
<DeleteForm user={user} penaltys={userPenaltys} />
</div>
</div>
);
}