feat(auth): complete sqlite auth and client invitations
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
"use server";
|
||||
|
||||
import { redirect } from "next/navigation";
|
||||
import {
|
||||
acceptPortalInvitation,
|
||||
PortalInvitationError,
|
||||
} from "@/server/auth/invitations";
|
||||
|
||||
export async function acceptInvitation(formData: FormData) {
|
||||
const token = String(formData.get("token") ?? "");
|
||||
const displayName = String(formData.get("displayName") ?? "");
|
||||
const password = String(formData.get("password") ?? "");
|
||||
|
||||
try {
|
||||
await acceptPortalInvitation({ token, displayName, password });
|
||||
} catch (error) {
|
||||
const message =
|
||||
error instanceof PortalInvitationError
|
||||
? error.message
|
||||
: "Portal hesabı oluşturulamadı.";
|
||||
redirect(`/invite/${encodeURIComponent(token)}?error=true&message=${encodeURIComponent(message)}`);
|
||||
}
|
||||
|
||||
redirect(
|
||||
`/login?message=${encodeURIComponent("Portal hesabın oluşturuldu. Şimdi giriş yapabilirsin.")}`,
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
import { LockKeyhole, Mail, UserRound } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { acceptInvitation } from "./actions";
|
||||
import { AuthPageShell } from "@/components/auth/auth-page-shell";
|
||||
import { SubmitButton } from "@/components/auth/submit-button";
|
||||
import { ErrorToaster } from "@/components/error-toaster";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/field";
|
||||
import { getPortalInvitationPreview } from "@/server/auth/invitations";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export default async function InvitationPage({
|
||||
params,
|
||||
searchParams,
|
||||
}: {
|
||||
params: Promise<{ token: string }>;
|
||||
searchParams: Promise<{ error?: string; message?: string }>;
|
||||
}) {
|
||||
const { token } = await params;
|
||||
const invitation = getPortalInvitationPreview(token);
|
||||
|
||||
if (!invitation) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const query = await searchParams;
|
||||
const isUsable = invitation.status === "pending";
|
||||
const unavailableMessage =
|
||||
invitation.status === "expired"
|
||||
? "Bu davetin süresi dolmuş. Freelancer'dan yeni bir bağlantı istemelisin."
|
||||
: invitation.status === "accepted"
|
||||
? "Bu davet daha önce kullanılmış. Hesabınla giriş yapabilirsin."
|
||||
: invitation.status === "revoked"
|
||||
? "Bu davet iptal edilmiş. Freelancer'dan yeni bir bağlantı istemelisin."
|
||||
: null;
|
||||
|
||||
return (
|
||||
<>
|
||||
{query.error && query.message ? <ErrorToaster message={query.message} /> : null}
|
||||
<AuthPageShell
|
||||
title="Müşteri portalına katıl"
|
||||
description="Davet edilen hesabın için adını ve şifreni belirle."
|
||||
form={
|
||||
isUsable ? (
|
||||
<form className="space-y-6">
|
||||
<input type="hidden" name="token" value={token} />
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="email" className="flex items-center gap-2">
|
||||
<Mail className="h-4 w-4 text-muted-foreground" />
|
||||
E-posta
|
||||
</Label>
|
||||
<Input id="email" type="email" value={invitation.email} disabled />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="displayName" className="flex items-center gap-2">
|
||||
<UserRound className="h-4 w-4 text-muted-foreground" />
|
||||
Ad soyad
|
||||
</Label>
|
||||
<Input id="displayName" name="displayName" required maxLength={120} />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="password" className="flex items-center gap-2">
|
||||
<LockKeyhole className="h-4 w-4 text-muted-foreground" />
|
||||
Şifre
|
||||
</Label>
|
||||
<Input id="password" name="password" type="password" required minLength={8} maxLength={128} />
|
||||
<p className="text-xs text-muted-foreground">En az 8 karakter kullan.</p>
|
||||
</div>
|
||||
</div>
|
||||
<SubmitButton formAction={acceptInvitation} className="h-11 w-full" pendingText="Hesap oluşturuluyor...">
|
||||
Portal hesabını oluştur
|
||||
</SubmitButton>
|
||||
</form>
|
||||
) : (
|
||||
<p className="text-sm text-muted-foreground">{unavailableMessage}</p>
|
||||
)
|
||||
}
|
||||
secondaryAction={null}
|
||||
footer={
|
||||
<Link href="/login" className="text-sm font-medium text-primary hover:text-primary-hover">
|
||||
Giriş sayfasına dön
|
||||
</Link>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user