removed console.log statements
This commit is contained in:
@@ -1,246 +1,245 @@
|
||||
'use client';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { StationOptionalDefaultsSchema } from '@repo/db/zod';
|
||||
import { set, useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
import { BosUse, Country, Station } from '@repo/db';
|
||||
import { FileText, LocateIcon, PlaneIcon } from 'lucide-react';
|
||||
import { Input } from '../../../../_components/ui/Input';
|
||||
import { useState } from 'react';
|
||||
import { deleteStation, upsertStation } from '../action';
|
||||
import { Button } from '../../../../_components/ui/Button';
|
||||
import { redirect } from 'next/navigation';
|
||||
"use client";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { StationOptionalDefaultsSchema } from "@repo/db/zod";
|
||||
import { set, useForm } from "react-hook-form";
|
||||
import { z } from "zod";
|
||||
import { BosUse, Country, Station } from "@repo/db";
|
||||
import { FileText, LocateIcon, PlaneIcon } from "lucide-react";
|
||||
import { Input } from "../../../../_components/ui/Input";
|
||||
import { useState } from "react";
|
||||
import { deleteStation, upsertStation } from "../action";
|
||||
import { Button } from "../../../../_components/ui/Button";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
export const StationForm = ({ station }: { station?: Station }) => {
|
||||
const form = useForm<z.infer<typeof StationOptionalDefaultsSchema>>({
|
||||
resolver: zodResolver(StationOptionalDefaultsSchema),
|
||||
defaultValues: station,
|
||||
});
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [deleteLoading, setDeleteLoading] = useState(false);
|
||||
console.log(form.formState.errors);
|
||||
return (
|
||||
<>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(async (values) => {
|
||||
setLoading(true);
|
||||
const createdStation = await upsertStation(values, station?.id);
|
||||
setLoading(false);
|
||||
if (!station) redirect(`/admin/station`);
|
||||
})}
|
||||
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-body">
|
||||
<h2 className="card-title">
|
||||
<FileText className="w-5 h-5" /> Allgemeines
|
||||
</h2>
|
||||
<Input
|
||||
form={form}
|
||||
label="BOS Rufname"
|
||||
name="bosCallsign"
|
||||
className="input-sm"
|
||||
/>
|
||||
<Input
|
||||
form={form}
|
||||
label="BOS Rufname (kurz)"
|
||||
name="bosCallsignShort"
|
||||
className="input-sm"
|
||||
/>
|
||||
<Input
|
||||
form={form}
|
||||
label="Betreiber"
|
||||
name="operator"
|
||||
className="input-sm"
|
||||
/>
|
||||
<Input
|
||||
form={form}
|
||||
label="ATC Rufname"
|
||||
name="atcCallsign"
|
||||
className="input-sm"
|
||||
/>
|
||||
<Input
|
||||
form={form}
|
||||
label="FIR (Flight Information Region)"
|
||||
name="fir"
|
||||
className="input-sm"
|
||||
/>
|
||||
<Input
|
||||
form={form}
|
||||
label="Leitstelle Rufname"
|
||||
name="bosRadioArea"
|
||||
className="input-sm"
|
||||
/>
|
||||
const form = useForm<z.infer<typeof StationOptionalDefaultsSchema>>({
|
||||
resolver: zodResolver(StationOptionalDefaultsSchema),
|
||||
defaultValues: station,
|
||||
});
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [deleteLoading, setDeleteLoading] = useState(false);
|
||||
return (
|
||||
<>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(async (values) => {
|
||||
setLoading(true);
|
||||
const createdStation = await upsertStation(values, station?.id);
|
||||
setLoading(false);
|
||||
if (!station) redirect(`/admin/station`);
|
||||
})}
|
||||
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-body">
|
||||
<h2 className="card-title">
|
||||
<FileText className="w-5 h-5" /> Allgemeines
|
||||
</h2>
|
||||
<Input
|
||||
form={form}
|
||||
label="BOS Rufname"
|
||||
name="bosCallsign"
|
||||
className="input-sm"
|
||||
/>
|
||||
<Input
|
||||
form={form}
|
||||
label="BOS Rufname (kurz)"
|
||||
name="bosCallsignShort"
|
||||
className="input-sm"
|
||||
/>
|
||||
<Input
|
||||
form={form}
|
||||
label="Betreiber"
|
||||
name="operator"
|
||||
className="input-sm"
|
||||
/>
|
||||
<Input
|
||||
form={form}
|
||||
label="ATC Rufname"
|
||||
name="atcCallsign"
|
||||
className="input-sm"
|
||||
/>
|
||||
<Input
|
||||
form={form}
|
||||
label="FIR (Flight Information Region)"
|
||||
name="fir"
|
||||
className="input-sm"
|
||||
/>
|
||||
<Input
|
||||
form={form}
|
||||
label="Leitstelle Rufname"
|
||||
name="bosRadioArea"
|
||||
className="input-sm"
|
||||
/>
|
||||
|
||||
<label className="form-control w-full">
|
||||
<span className="label-text text-lg flex items-center gap-2">
|
||||
BOS Nutzung
|
||||
</span>
|
||||
<select
|
||||
className="input-sm select select-bordered select-sm"
|
||||
{...form.register('bosUse')}
|
||||
>
|
||||
{Object.keys(BosUse).map((use) => (
|
||||
<option key={use} value={use}>
|
||||
{use}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card bg-base-200 shadow-xl col-span-2 max-xl:col-span-6">
|
||||
<div className="card-body">
|
||||
<h2 className="card-title">
|
||||
<LocateIcon className="w-5 h-5" /> Standort + Ausrüstung
|
||||
</h2>
|
||||
<label className="form-control w-full">
|
||||
<span className="label-text text-lg flex items-center gap-2">
|
||||
Land
|
||||
</span>
|
||||
<select
|
||||
className="input-sm select select-bordered select-sm"
|
||||
{...form.register('country', {})}
|
||||
>
|
||||
{Object.keys(Country).map((use) => (
|
||||
<option key={use} value={use}>
|
||||
{use}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<Input
|
||||
form={form}
|
||||
label="Bundesland"
|
||||
name="locationState"
|
||||
className="input-sm"
|
||||
/>
|
||||
<Input
|
||||
form={form}
|
||||
label="Bundesland (kurz)"
|
||||
name="locationStateShort"
|
||||
className="input-sm"
|
||||
/>
|
||||
<span className="label-text text-lg flex items-center gap-2">
|
||||
Ausgerüstet mit:
|
||||
</span>
|
||||
<div className="form-control">
|
||||
<label className="label cursor-pointer">
|
||||
<span>Winde</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
className="toggle"
|
||||
{...form.register('hasWinch')}
|
||||
/>
|
||||
</label>
|
||||
<label className="label cursor-pointer">
|
||||
<span>Nachtsicht Gerät</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
className="toggle"
|
||||
{...form.register('hasNvg')}
|
||||
/>
|
||||
</label>
|
||||
<label className="label cursor-pointer">
|
||||
<span>24-Stunden Einsatzfähig</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
className="toggle"
|
||||
{...form.register('is24h')}
|
||||
/>
|
||||
</label>
|
||||
<label className="label cursor-pointer">
|
||||
<span>Bergetau</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
className="toggle"
|
||||
{...form.register('hasRope')}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<label className="form-control w-full">
|
||||
<span className="label-text text-lg flex items-center gap-2">
|
||||
BOS Nutzung
|
||||
</span>
|
||||
<select
|
||||
className="input-sm select select-bordered select-sm"
|
||||
{...form.register("bosUse")}
|
||||
>
|
||||
{Object.keys(BosUse).map((use) => (
|
||||
<option key={use} value={use}>
|
||||
{use}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card bg-base-200 shadow-xl col-span-2 max-xl:col-span-6">
|
||||
<div className="card-body">
|
||||
<h2 className="card-title">
|
||||
<LocateIcon className="w-5 h-5" /> Standort + Ausrüstung
|
||||
</h2>
|
||||
<label className="form-control w-full">
|
||||
<span className="label-text text-lg flex items-center gap-2">
|
||||
Land
|
||||
</span>
|
||||
<select
|
||||
className="input-sm select select-bordered select-sm"
|
||||
{...form.register("country", {})}
|
||||
>
|
||||
{Object.keys(Country).map((use) => (
|
||||
<option key={use} value={use}>
|
||||
{use}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<Input
|
||||
form={form}
|
||||
label="Bundesland"
|
||||
name="locationState"
|
||||
className="input-sm"
|
||||
/>
|
||||
<Input
|
||||
form={form}
|
||||
label="Bundesland (kurz)"
|
||||
name="locationStateShort"
|
||||
className="input-sm"
|
||||
/>
|
||||
<span className="label-text text-lg flex items-center gap-2">
|
||||
Ausgerüstet mit:
|
||||
</span>
|
||||
<div className="form-control">
|
||||
<label className="label cursor-pointer">
|
||||
<span>Winde</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
className="toggle"
|
||||
{...form.register("hasWinch")}
|
||||
/>
|
||||
</label>
|
||||
<label className="label cursor-pointer">
|
||||
<span>Nachtsicht Gerät</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
className="toggle"
|
||||
{...form.register("hasNvg")}
|
||||
/>
|
||||
</label>
|
||||
<label className="label cursor-pointer">
|
||||
<span>24-Stunden Einsatzfähig</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
className="toggle"
|
||||
{...form.register("is24h")}
|
||||
/>
|
||||
</label>
|
||||
<label className="label cursor-pointer">
|
||||
<span>Bergetau</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
className="toggle"
|
||||
{...form.register("hasRope")}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<Input
|
||||
form={form}
|
||||
label="Breitengrad"
|
||||
name="latitude"
|
||||
className="input-sm"
|
||||
formOptions={{ valueAsNumber: true }}
|
||||
type="number"
|
||||
step="any"
|
||||
/>
|
||||
<Input
|
||||
form={form}
|
||||
label="Längengrad"
|
||||
name="longitude"
|
||||
className="input-sm"
|
||||
formOptions={{ valueAsNumber: true }}
|
||||
type="number"
|
||||
step="any"
|
||||
/>
|
||||
<label className="label cursor-pointer">
|
||||
<span className="text-lg">Reichweiten ausblenden</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
className="toggle"
|
||||
{...form.register('hideRangeRings')}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card bg-base-200 shadow-xl col-span-2 max-xl:col-span-6">
|
||||
<div className="card-body">
|
||||
<h2 className="card-title">
|
||||
<PlaneIcon className="w-5 h-5" /> Hubschrauber
|
||||
</h2>
|
||||
<Input
|
||||
form={form}
|
||||
label="Hubschrauber Typ"
|
||||
name="aircraft"
|
||||
className="input-sm"
|
||||
/>
|
||||
<Input
|
||||
form={form}
|
||||
formOptions={{ valueAsNumber: true }}
|
||||
type="number"
|
||||
label="Hubschrauber Geschwindigkeit"
|
||||
className="input-sm"
|
||||
name="aircraftSpeed"
|
||||
/>
|
||||
<Input
|
||||
form={form}
|
||||
label="Hubschrauber Registrierung"
|
||||
name="aircraftRegistration"
|
||||
className="input-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card bg-base-200 shadow-xl col-span-6">
|
||||
<div className="card-body ">
|
||||
<div className="flex w-full gap-4">
|
||||
<Button
|
||||
isLoading={loading}
|
||||
type="submit"
|
||||
className="btn btn-primary flex-1"
|
||||
>
|
||||
Speichern
|
||||
</Button>
|
||||
{station && (
|
||||
<Button
|
||||
isLoading={deleteLoading}
|
||||
onClick={async () => {
|
||||
setDeleteLoading(true);
|
||||
await deleteStation(station.id);
|
||||
redirect('/admin/station');
|
||||
}}
|
||||
className="btn btn-error"
|
||||
>
|
||||
Löschen
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
<Input
|
||||
form={form}
|
||||
label="Breitengrad"
|
||||
name="latitude"
|
||||
className="input-sm"
|
||||
formOptions={{ valueAsNumber: true }}
|
||||
type="number"
|
||||
step="any"
|
||||
/>
|
||||
<Input
|
||||
form={form}
|
||||
label="Längengrad"
|
||||
name="longitude"
|
||||
className="input-sm"
|
||||
formOptions={{ valueAsNumber: true }}
|
||||
type="number"
|
||||
step="any"
|
||||
/>
|
||||
<label className="label cursor-pointer">
|
||||
<span className="text-lg">Reichweiten ausblenden</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
className="toggle"
|
||||
{...form.register("hideRangeRings")}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card bg-base-200 shadow-xl col-span-2 max-xl:col-span-6">
|
||||
<div className="card-body">
|
||||
<h2 className="card-title">
|
||||
<PlaneIcon className="w-5 h-5" /> Hubschrauber
|
||||
</h2>
|
||||
<Input
|
||||
form={form}
|
||||
label="Hubschrauber Typ"
|
||||
name="aircraft"
|
||||
className="input-sm"
|
||||
/>
|
||||
<Input
|
||||
form={form}
|
||||
formOptions={{ valueAsNumber: true }}
|
||||
type="number"
|
||||
label="Hubschrauber Geschwindigkeit"
|
||||
className="input-sm"
|
||||
name="aircraftSpeed"
|
||||
/>
|
||||
<Input
|
||||
form={form}
|
||||
label="Hubschrauber Registrierung"
|
||||
name="aircraftRegistration"
|
||||
className="input-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card bg-base-200 shadow-xl col-span-6">
|
||||
<div className="card-body ">
|
||||
<div className="flex w-full gap-4">
|
||||
<Button
|
||||
isLoading={loading}
|
||||
type="submit"
|
||||
className="btn btn-primary flex-1"
|
||||
>
|
||||
Speichern
|
||||
</Button>
|
||||
{station && (
|
||||
<Button
|
||||
isLoading={deleteLoading}
|
||||
onClick={async () => {
|
||||
setDeleteLoading(true);
|
||||
await deleteStation(station.id);
|
||||
redirect("/admin/station");
|
||||
}}
|
||||
className="btn btn-error"
|
||||
>
|
||||
Löschen
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
import { PrismaClient } from '@repo/db';
|
||||
import { PrismaClient } from "@repo/db";
|
||||
|
||||
export default async ({ params }: { params: Promise<{ id: string }> }) => {
|
||||
const prisma = new PrismaClient();
|
||||
const { id } = await params;
|
||||
const prisma = new PrismaClient();
|
||||
const { id } = await params;
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
where: {
|
||||
id: id,
|
||||
},
|
||||
});
|
||||
console.log(user);
|
||||
return (
|
||||
<div>
|
||||
<h1>
|
||||
{user?.firstname} {user?.lastname}
|
||||
</h1>
|
||||
<p>{user?.email}</p>
|
||||
{/* TODO: Hier Nutzerdaten bearbeiten */}
|
||||
</div>
|
||||
);
|
||||
const user = await prisma.user.findUnique({
|
||||
where: {
|
||||
id: id,
|
||||
},
|
||||
});
|
||||
console.log(user);
|
||||
return (
|
||||
<div>
|
||||
<h1>
|
||||
{user?.firstname} {user?.lastname}
|
||||
</h1>
|
||||
<p>{user?.email}</p>
|
||||
{/* TODO: Hier Nutzerdaten bearbeiten */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -10,345 +10,344 @@ import { useSession } from "next-auth/react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { Button } from "../../../_components/ui/Button";
|
||||
import {
|
||||
PersonIcon,
|
||||
EnvelopeClosedIcon,
|
||||
BookmarkIcon,
|
||||
DiscordLogoIcon,
|
||||
PaperPlaneIcon,
|
||||
Link2Icon,
|
||||
MixerHorizontalIcon,
|
||||
LockClosedIcon,
|
||||
LockOpen2Icon,
|
||||
LockOpen1Icon,
|
||||
PersonIcon,
|
||||
EnvelopeClosedIcon,
|
||||
BookmarkIcon,
|
||||
DiscordLogoIcon,
|
||||
PaperPlaneIcon,
|
||||
Link2Icon,
|
||||
MixerHorizontalIcon,
|
||||
LockClosedIcon,
|
||||
LockOpen2Icon,
|
||||
LockOpen1Icon,
|
||||
} from "@radix-ui/react-icons";
|
||||
|
||||
export const ProfileForm = ({ user }: { user: User }) => {
|
||||
const schema = z.object({
|
||||
firstname: z.string().min(2).max(30),
|
||||
lastname: z.string().min(2).max(30),
|
||||
email: z.string().email({
|
||||
message: "Bitte gebe eine gültige E-Mail Adresse ein",
|
||||
}),
|
||||
});
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
type IFormInput = z.infer<typeof schema>;
|
||||
const schema = z.object({
|
||||
firstname: z.string().min(2).max(30),
|
||||
lastname: z.string().min(2).max(30),
|
||||
email: z.string().email({
|
||||
message: "Bitte gebe eine gültige E-Mail Adresse ein",
|
||||
}),
|
||||
});
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
type IFormInput = z.infer<typeof schema>;
|
||||
|
||||
const form = useForm<IFormInput>({
|
||||
defaultValues: {
|
||||
firstname: user.firstname,
|
||||
lastname: user.lastname,
|
||||
email: user.email,
|
||||
},
|
||||
resolver: zodResolver(schema),
|
||||
});
|
||||
return (
|
||||
<form
|
||||
className="card-body"
|
||||
onSubmit={form.handleSubmit(async (values) => {
|
||||
setIsLoading(true);
|
||||
await updateUser(values);
|
||||
form.reset(values);
|
||||
setIsLoading(false);
|
||||
toast.success("Deine Änderungen wurden gespeichert!", {
|
||||
style: {
|
||||
background: "var(--color-base-100)",
|
||||
color: "var(--color-base-content)",
|
||||
},
|
||||
});
|
||||
})}
|
||||
>
|
||||
<h2 className="card-title">
|
||||
<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>
|
||||
)}
|
||||
<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="floating-label w-full">
|
||||
<span className="text-lg flex items-center gap-2">
|
||||
<EnvelopeClosedIcon /> E-Mail
|
||||
</span>
|
||||
<input
|
||||
{...form.register("email")}
|
||||
type="text"
|
||||
className="input input-bordered w-full"
|
||||
defaultValue={user.email}
|
||||
placeholder="E-Mail"
|
||||
/>
|
||||
</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"
|
||||
className="btn-sm btn-wide btn-outline btn-primary"
|
||||
disabled={!form.formState.isDirty}
|
||||
isLoading={isLoading}
|
||||
>
|
||||
<BookmarkIcon /> Speichern
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
const form = useForm<IFormInput>({
|
||||
defaultValues: {
|
||||
firstname: user.firstname,
|
||||
lastname: user.lastname,
|
||||
email: user.email,
|
||||
},
|
||||
resolver: zodResolver(schema),
|
||||
});
|
||||
return (
|
||||
<form
|
||||
className="card-body"
|
||||
onSubmit={form.handleSubmit(async (values) => {
|
||||
setIsLoading(true);
|
||||
await updateUser(values);
|
||||
form.reset(values);
|
||||
setIsLoading(false);
|
||||
toast.success("Deine Änderungen wurden gespeichert!", {
|
||||
style: {
|
||||
background: "var(--color-base-100)",
|
||||
color: "var(--color-base-content)",
|
||||
},
|
||||
});
|
||||
})}
|
||||
>
|
||||
<h2 className="card-title">
|
||||
<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>
|
||||
)}
|
||||
<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="floating-label w-full">
|
||||
<span className="text-lg flex items-center gap-2">
|
||||
<EnvelopeClosedIcon /> E-Mail
|
||||
</span>
|
||||
<input
|
||||
{...form.register("email")}
|
||||
type="text"
|
||||
className="input input-bordered w-full"
|
||||
defaultValue={user.email}
|
||||
placeholder="E-Mail"
|
||||
/>
|
||||
</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"
|
||||
className="btn-sm btn-wide btn-outline btn-primary"
|
||||
disabled={!form.formState.isDirty}
|
||||
isLoading={isLoading}
|
||||
>
|
||||
<BookmarkIcon /> Speichern
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export const SocialForm = ({
|
||||
discordAccount,
|
||||
user,
|
||||
discordAccount,
|
||||
user,
|
||||
}: {
|
||||
discordAccount?: DiscordAccount;
|
||||
user: User;
|
||||
discordAccount?: DiscordAccount;
|
||||
user: User;
|
||||
}) => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [vatsimLoading, setVatsimLoading] = useState(false);
|
||||
const router = useRouter();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [vatsimLoading, setVatsimLoading] = useState(false);
|
||||
const router = useRouter();
|
||||
|
||||
const schema = z.object({
|
||||
vatsimCid: z.number().min(1000).max(9999999),
|
||||
});
|
||||
const schema = z.object({
|
||||
vatsimCid: z.number().min(1000).max(9999999),
|
||||
});
|
||||
|
||||
type IFormInput = z.infer<typeof schema>;
|
||||
type IFormInput = z.infer<typeof schema>;
|
||||
|
||||
const form = useForm<IFormInput>({
|
||||
defaultValues: {
|
||||
vatsimCid: user?.vatsimCid || undefined,
|
||||
},
|
||||
resolver: zodResolver(schema),
|
||||
});
|
||||
const form = useForm<IFormInput>({
|
||||
defaultValues: {
|
||||
vatsimCid: user?.vatsimCid || undefined,
|
||||
},
|
||||
resolver: zodResolver(schema),
|
||||
});
|
||||
|
||||
if (!user) return null;
|
||||
console.log("Dirty", form.formState.isDirty);
|
||||
return (
|
||||
<form
|
||||
className="card-body"
|
||||
onSubmit={form.handleSubmit(async (values) => {
|
||||
form.handleSubmit(async () => {
|
||||
setVatsimLoading(true);
|
||||
});
|
||||
await updateUser({
|
||||
vatsimCid: values.vatsimCid,
|
||||
});
|
||||
setVatsimLoading(false);
|
||||
form.reset(values);
|
||||
toast.success("Deine Änderungen wurden gespeichert!", {
|
||||
style: {
|
||||
background: "var(--color-base-100)",
|
||||
color: "var(--color-base-content)",
|
||||
},
|
||||
});
|
||||
})}
|
||||
>
|
||||
<h2 className="card-title">
|
||||
<Link2Icon className="w-5 h-5" /> Verbindungen & Benachrichtigungen
|
||||
</h2>
|
||||
<div>
|
||||
<div>
|
||||
<div className="label">
|
||||
<span className="label-text text-lg flex items-center gap-2">
|
||||
<DiscordLogoIcon /> Discord
|
||||
</span>
|
||||
</div>
|
||||
if (!user) return null;
|
||||
return (
|
||||
<form
|
||||
className="card-body"
|
||||
onSubmit={form.handleSubmit(async (values) => {
|
||||
form.handleSubmit(async () => {
|
||||
setVatsimLoading(true);
|
||||
});
|
||||
await updateUser({
|
||||
vatsimCid: values.vatsimCid,
|
||||
});
|
||||
setVatsimLoading(false);
|
||||
form.reset(values);
|
||||
toast.success("Deine Änderungen wurden gespeichert!", {
|
||||
style: {
|
||||
background: "var(--color-base-100)",
|
||||
color: "var(--color-base-content)",
|
||||
},
|
||||
});
|
||||
})}
|
||||
>
|
||||
<h2 className="card-title">
|
||||
<Link2Icon className="w-5 h-5" /> Verbindungen & Benachrichtigungen
|
||||
</h2>
|
||||
<div>
|
||||
<div>
|
||||
<div className="label">
|
||||
<span className="label-text text-lg flex items-center gap-2">
|
||||
<DiscordLogoIcon /> Discord
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{discordAccount ? (
|
||||
<Button
|
||||
className="btn-success btn-block btn-outline group transition-all duration-0 hover:btn-error"
|
||||
isLoading={isLoading}
|
||||
onClick={async () => {
|
||||
setIsLoading(true);
|
||||
await unlinkDiscord(user.id);
|
||||
router.refresh();
|
||||
setIsLoading(false);
|
||||
}}
|
||||
>
|
||||
<DiscordLogoIcon className="w-5 h-5" />
|
||||
<span className="group-hover:hidden">
|
||||
Verbunden mit {discordAccount.username}
|
||||
</span>
|
||||
<span className="hidden group-hover:inline">
|
||||
Verbindung trennen{isLoading && "..."}
|
||||
</span>
|
||||
</Button>
|
||||
) : (
|
||||
<a href={process.env.NEXT_PUBLIC_DISCORD_URL}>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-primary btn-block"
|
||||
onSubmit={() => false}
|
||||
>
|
||||
<DiscordLogoIcon className="w-5 h-5" /> Mit Discord verbinden
|
||||
</button>
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="content-center">
|
||||
<label className="floating-label w-full mt-5">
|
||||
<span className="text-lg flex items-center gap-2">
|
||||
<PaperPlaneIcon /> VATSIM-CID
|
||||
</span>
|
||||
<input
|
||||
type="number"
|
||||
className="input input-bordered w-full"
|
||||
placeholder="1445241"
|
||||
defaultValue={user.vatsimCid as number | undefined}
|
||||
{...form.register("vatsimCid", {
|
||||
valueAsNumber: true,
|
||||
})}
|
||||
/>
|
||||
</label>
|
||||
{form.formState.errors.vatsimCid && (
|
||||
<p className="text-error">
|
||||
{form.formState.errors.vatsimCid.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="card-actions justify-center pt-6 mt-auto">
|
||||
<Button
|
||||
className="btn-sm btn-wide btn-outline btn-primary"
|
||||
isLoading={vatsimLoading}
|
||||
disabled={!form.formState.isDirty}
|
||||
role="submit"
|
||||
>
|
||||
<BookmarkIcon /> Speichern
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
{discordAccount ? (
|
||||
<Button
|
||||
className="btn-success btn-block btn-outline group transition-all duration-0 hover:btn-error"
|
||||
isLoading={isLoading}
|
||||
onClick={async () => {
|
||||
setIsLoading(true);
|
||||
await unlinkDiscord(user.id);
|
||||
router.refresh();
|
||||
setIsLoading(false);
|
||||
}}
|
||||
>
|
||||
<DiscordLogoIcon className="w-5 h-5" />
|
||||
<span className="group-hover:hidden">
|
||||
Verbunden mit {discordAccount.username}
|
||||
</span>
|
||||
<span className="hidden group-hover:inline">
|
||||
Verbindung trennen{isLoading && "..."}
|
||||
</span>
|
||||
</Button>
|
||||
) : (
|
||||
<a href={process.env.NEXT_PUBLIC_DISCORD_URL}>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-primary btn-block"
|
||||
onSubmit={() => false}
|
||||
>
|
||||
<DiscordLogoIcon className="w-5 h-5" /> Mit Discord verbinden
|
||||
</button>
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="content-center">
|
||||
<label className="floating-label w-full mt-5">
|
||||
<span className="text-lg flex items-center gap-2">
|
||||
<PaperPlaneIcon /> VATSIM-CID
|
||||
</span>
|
||||
<input
|
||||
type="number"
|
||||
className="input input-bordered w-full"
|
||||
placeholder="1445241"
|
||||
defaultValue={user.vatsimCid as number | undefined}
|
||||
{...form.register("vatsimCid", {
|
||||
valueAsNumber: true,
|
||||
})}
|
||||
/>
|
||||
</label>
|
||||
{form.formState.errors.vatsimCid && (
|
||||
<p className="text-error">
|
||||
{form.formState.errors.vatsimCid.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="card-actions justify-center pt-6 mt-auto">
|
||||
<Button
|
||||
className="btn-sm btn-wide btn-outline btn-primary"
|
||||
isLoading={vatsimLoading}
|
||||
disabled={!form.formState.isDirty}
|
||||
role="submit"
|
||||
>
|
||||
<BookmarkIcon /> Speichern
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export const PasswordForm = ({ user }: { user: User }) => {
|
||||
const schema = z.object({
|
||||
password: z.string().min(2).max(30),
|
||||
newPassword: z.string().min(2).max(30),
|
||||
newPasswordConfirm: z.string().min(2).max(30),
|
||||
});
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
type IFormInput = z.infer<typeof schema>;
|
||||
const schema = z.object({
|
||||
password: z.string().min(2).max(30),
|
||||
newPassword: z.string().min(2).max(30),
|
||||
newPasswordConfirm: z.string().min(2).max(30),
|
||||
});
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
type IFormInput = z.infer<typeof schema>;
|
||||
|
||||
const form = useForm<IFormInput>({
|
||||
defaultValues: {},
|
||||
resolver: zodResolver(schema),
|
||||
});
|
||||
return (
|
||||
<form
|
||||
className="card-body"
|
||||
onSubmit={form.handleSubmit(async (values) => {
|
||||
setIsLoading(true);
|
||||
const result = await changePassword(
|
||||
values.password,
|
||||
values.newPassword
|
||||
);
|
||||
form.reset(values);
|
||||
setIsLoading(false);
|
||||
if (result.error) {
|
||||
form.setError("password", {
|
||||
type: "manual",
|
||||
message: result.error,
|
||||
});
|
||||
} else if (result.success) {
|
||||
toast.success("Dein Passwort wurde geändert!", {
|
||||
style: {
|
||||
background: "var(--color-base-100)",
|
||||
color: "var(--color-base-content)",
|
||||
},
|
||||
});
|
||||
}
|
||||
})}
|
||||
>
|
||||
<h2 className="card-title">
|
||||
<LockClosedIcon className="w-5 h-5" /> Password Ändern
|
||||
</h2>
|
||||
<div className="">
|
||||
<label className="floating-label w-full mt-5 mb-5">
|
||||
<span className="text-lg flex items-center gap-2">
|
||||
<LockOpen2Icon /> Aktuelles Passwort
|
||||
</span>
|
||||
<input
|
||||
{...form.register("password")}
|
||||
type="text"
|
||||
className="input input-bordered w-full"
|
||||
defaultValue={""}
|
||||
/>
|
||||
</label>
|
||||
{form.formState.errors.password && (
|
||||
<p className="text-error">{form.formState.errors.password.message}</p>
|
||||
)}
|
||||
<label className="floating-label w-full mb-5">
|
||||
<span className="text-lg flex items-center gap-2">
|
||||
<LockOpen1Icon /> Neues Passwort
|
||||
</span>
|
||||
<input
|
||||
{...form.register("newPassword")}
|
||||
type="text"
|
||||
className="input input-bordered w-full"
|
||||
defaultValue={""}
|
||||
/>
|
||||
</label>
|
||||
{form.formState.errors.newPassword && (
|
||||
<p className="text-error">
|
||||
{form.formState.errors.newPassword?.message}
|
||||
</p>
|
||||
)}
|
||||
<label className="floating-label w-full">
|
||||
<span className="text-lg flex items-center gap-2">
|
||||
<LockOpen1Icon /> Passwort wiederholen
|
||||
</span>
|
||||
<input
|
||||
{...form.register("newPasswordConfirm")}
|
||||
type="text"
|
||||
className="input input-bordered w-full"
|
||||
defaultValue={""}
|
||||
/>
|
||||
</label>
|
||||
{form.formState.errors.newPasswordConfirm && (
|
||||
<p className="text-error">
|
||||
{form.formState.errors.newPasswordConfirm?.message}
|
||||
</p>
|
||||
)}
|
||||
<div className="card-actions justify-center pt-6">
|
||||
<Button
|
||||
role="submit"
|
||||
className="btn-sm btn-wide btn-outline btn-primary"
|
||||
disabled={!form.formState.isDirty}
|
||||
isLoading={isLoading}
|
||||
>
|
||||
<BookmarkIcon /> Speichern
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
const form = useForm<IFormInput>({
|
||||
defaultValues: {},
|
||||
resolver: zodResolver(schema),
|
||||
});
|
||||
return (
|
||||
<form
|
||||
className="card-body"
|
||||
onSubmit={form.handleSubmit(async (values) => {
|
||||
setIsLoading(true);
|
||||
const result = await changePassword(
|
||||
values.password,
|
||||
values.newPassword,
|
||||
);
|
||||
form.reset(values);
|
||||
setIsLoading(false);
|
||||
if (result.error) {
|
||||
form.setError("password", {
|
||||
type: "manual",
|
||||
message: result.error,
|
||||
});
|
||||
} else if (result.success) {
|
||||
toast.success("Dein Passwort wurde geändert!", {
|
||||
style: {
|
||||
background: "var(--color-base-100)",
|
||||
color: "var(--color-base-content)",
|
||||
},
|
||||
});
|
||||
}
|
||||
})}
|
||||
>
|
||||
<h2 className="card-title">
|
||||
<LockClosedIcon className="w-5 h-5" /> Password Ändern
|
||||
</h2>
|
||||
<div className="">
|
||||
<label className="floating-label w-full mt-5 mb-5">
|
||||
<span className="text-lg flex items-center gap-2">
|
||||
<LockOpen2Icon /> Aktuelles Passwort
|
||||
</span>
|
||||
<input
|
||||
{...form.register("password")}
|
||||
type="text"
|
||||
className="input input-bordered w-full"
|
||||
defaultValue={""}
|
||||
/>
|
||||
</label>
|
||||
{form.formState.errors.password && (
|
||||
<p className="text-error">{form.formState.errors.password.message}</p>
|
||||
)}
|
||||
<label className="floating-label w-full mb-5">
|
||||
<span className="text-lg flex items-center gap-2">
|
||||
<LockOpen1Icon /> Neues Passwort
|
||||
</span>
|
||||
<input
|
||||
{...form.register("newPassword")}
|
||||
type="text"
|
||||
className="input input-bordered w-full"
|
||||
defaultValue={""}
|
||||
/>
|
||||
</label>
|
||||
{form.formState.errors.newPassword && (
|
||||
<p className="text-error">
|
||||
{form.formState.errors.newPassword?.message}
|
||||
</p>
|
||||
)}
|
||||
<label className="floating-label w-full">
|
||||
<span className="text-lg flex items-center gap-2">
|
||||
<LockOpen1Icon /> Passwort wiederholen
|
||||
</span>
|
||||
<input
|
||||
{...form.register("newPasswordConfirm")}
|
||||
type="text"
|
||||
className="input input-bordered w-full"
|
||||
defaultValue={""}
|
||||
/>
|
||||
</label>
|
||||
{form.formState.errors.newPasswordConfirm && (
|
||||
<p className="text-error">
|
||||
{form.formState.errors.newPasswordConfirm?.message}
|
||||
</p>
|
||||
)}
|
||||
<div className="card-actions justify-center pt-6">
|
||||
<Button
|
||||
role="submit"
|
||||
className="btn-sm btn-wide btn-outline btn-primary"
|
||||
disabled={!form.formState.isDirty}
|
||||
isLoading={isLoading}
|
||||
>
|
||||
<BookmarkIcon /> Speichern
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -64,7 +64,6 @@ export function PaginatedTable<TData>({
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
refresh: () => {
|
||||
console.log("refresh");
|
||||
RefreshTableData();
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -1,67 +1,66 @@
|
||||
import {
|
||||
AuthOptions,
|
||||
getServerSession as getNextAuthServerSession,
|
||||
} from 'next-auth';
|
||||
import { PrismaAdapter } from '@next-auth/prisma-adapter';
|
||||
import Credentials from 'next-auth/providers/credentials';
|
||||
import { PrismaClient } from '@repo/db';
|
||||
import bcrypt from 'bcryptjs';
|
||||
AuthOptions,
|
||||
getServerSession as getNextAuthServerSession,
|
||||
} from "next-auth";
|
||||
import { PrismaAdapter } from "@next-auth/prisma-adapter";
|
||||
import Credentials from "next-auth/providers/credentials";
|
||||
import { PrismaClient } from "@repo/db";
|
||||
import bcrypt from "bcryptjs";
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
export const options: AuthOptions = {
|
||||
providers: [
|
||||
Credentials({
|
||||
credentials: {
|
||||
email: { label: 'Email', type: 'email', placeholder: 'E-Mail' },
|
||||
password: { label: 'Password', type: 'password' },
|
||||
},
|
||||
async authorize(credentials, req) {
|
||||
try {
|
||||
if (!credentials) throw new Error('No credentials provided');
|
||||
const user = await prisma.user.findFirstOrThrow({
|
||||
where: { email: credentials.email },
|
||||
});
|
||||
if (bcrypt.compareSync(credentials.password, user.password)) {
|
||||
console.log('User found and password correct', user);
|
||||
return user;
|
||||
}
|
||||
return null;
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
}),
|
||||
],
|
||||
secret: process.env.NEXTAUTH_SECRET,
|
||||
session: {
|
||||
strategy: 'jwt',
|
||||
maxAge: 30 * 24 * 60 * 60,
|
||||
},
|
||||
providers: [
|
||||
Credentials({
|
||||
credentials: {
|
||||
email: { label: "Email", type: "email", placeholder: "E-Mail" },
|
||||
password: { label: "Password", type: "password" },
|
||||
},
|
||||
async authorize(credentials, req) {
|
||||
try {
|
||||
if (!credentials) throw new Error("No credentials provided");
|
||||
const user = await prisma.user.findFirstOrThrow({
|
||||
where: { email: credentials.email },
|
||||
});
|
||||
if (bcrypt.compareSync(credentials.password, user.password)) {
|
||||
return user;
|
||||
}
|
||||
return null;
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
}),
|
||||
],
|
||||
secret: process.env.NEXTAUTH_SECRET,
|
||||
session: {
|
||||
strategy: "jwt",
|
||||
maxAge: 30 * 24 * 60 * 60,
|
||||
},
|
||||
|
||||
adapter: PrismaAdapter(prisma as any),
|
||||
callbacks: {
|
||||
jwt: async ({ token, user }) => {
|
||||
if (user && 'firstname' in user) {
|
||||
return {
|
||||
...token,
|
||||
...user,
|
||||
};
|
||||
}
|
||||
return token;
|
||||
},
|
||||
session: async ({ session, user, token }) => {
|
||||
return {
|
||||
...session,
|
||||
user: token,
|
||||
};
|
||||
},
|
||||
},
|
||||
pages: {
|
||||
signIn: '/login',
|
||||
signOut: '/logout',
|
||||
error: '/authError',
|
||||
newUser: '/register',
|
||||
},
|
||||
adapter: PrismaAdapter(prisma as any),
|
||||
callbacks: {
|
||||
jwt: async ({ token, user }) => {
|
||||
if (user && "firstname" in user) {
|
||||
return {
|
||||
...token,
|
||||
...user,
|
||||
};
|
||||
}
|
||||
return token;
|
||||
},
|
||||
session: async ({ session, user, token }) => {
|
||||
return {
|
||||
...session,
|
||||
user: token,
|
||||
};
|
||||
},
|
||||
},
|
||||
pages: {
|
||||
signIn: "/login",
|
||||
signOut: "/logout",
|
||||
error: "/authError",
|
||||
newUser: "/register",
|
||||
},
|
||||
} satisfies AuthOptions;
|
||||
|
||||
export const getServerSession = async () => getNextAuthServerSession(options);
|
||||
|
||||
@@ -5,15 +5,11 @@ import { services } from "../../../(auth)/oauth/page";
|
||||
|
||||
export const POST = async (req: NextRequest) => {
|
||||
const form = new URLSearchParams(await req.text());
|
||||
console.log("POST body:");
|
||||
const client = new PrismaClient();
|
||||
const accessToken = form.get("token") || form.get("code");
|
||||
const clientId = form.get("client_id");
|
||||
const clientSecret = form.get("client_secret");
|
||||
|
||||
console.log("Access token:", accessToken);
|
||||
console.log("Client ID:", clientId);
|
||||
console.log("Secret:", clientSecret);
|
||||
const service = services.find((s) => s.id === clientId);
|
||||
|
||||
if (!accessToken)
|
||||
|
||||
Reference in New Issue
Block a user