init: lab project bootstrapped from isletmem-kovakcrm
- CRM domain modules removed (customers, services, software, calendar, tasks, invoices, leads, finance, etc.)
- DLS branding: package name=lab, logo wordmark, sidebar nav, header CTA
- Tenant layer extended with kind dimension (lab|clinic) + requireTenantKind helper
- Schema rewritten for DLS domain: jobs, job_files, job_status_history, prosthetics, connections, finance_entries, notifications
- Onboarding form: clinic/lab account-type selection + auto-generated memberNumber
- Placeholder routes for jobs/{inbound,outbound,new}, products, finance, connections
- PDF spec + spec.md under belgeler/
- db: lab database + 13 collections + indexes + storage bucket (job-files) provisioned via Appwrite MCP
Ref: belgeler/dls-ui-tasarim.pdf
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
"use client"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { useRouter } from "next/navigation"
|
||||
import Image from "next/image"
|
||||
|
||||
export function ForbiddenError() {
|
||||
const router = useRouter()
|
||||
|
||||
return (
|
||||
<div className='mx-auto flex min-h-dvh flex-col items-center justify-center gap-8 p-8 md:gap-12 md:p-16'>
|
||||
<Image
|
||||
src='https://ui.shadcn.com/placeholder.svg'
|
||||
alt='placeholder image'
|
||||
width={960}
|
||||
height={540}
|
||||
className='aspect-video w-240 rounded-xl object-cover dark:brightness-[0.95] dark:invert'
|
||||
/>
|
||||
<div className='text-center'>
|
||||
<h1 className='mb-4 text-3xl font-bold'>403</h1>
|
||||
<h2 className="mb-3 text-2xl font-semibold">Forbidden</h2>
|
||||
<p>Access to this resource is forbidden. You don't have the necessary permissions to view this page.</p>
|
||||
<div className='mt-6 flex items-center justify-center gap-4 md:mt-8'>
|
||||
<Button className='cursor-pointer' onClick={() => router.push('/dashboard')}>Go Back Home</Button>
|
||||
<Button variant='outline' className='flex cursor-pointer items-center gap-1' onClick={() => router.push('#')}>
|
||||
Contact Us
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { ForbiddenError } from "./components/forbidden-error"
|
||||
|
||||
export default function ForbiddenPage() {
|
||||
return <ForbiddenError />
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
"use client"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { useRouter } from "next/navigation"
|
||||
import Image from "next/image"
|
||||
|
||||
export function InternalServerError() {
|
||||
const router = useRouter()
|
||||
|
||||
return (
|
||||
<div className='mx-auto flex min-h-dvh flex-col items-center justify-center gap-8 p-8 md:gap-12 md:p-16'>
|
||||
<Image
|
||||
src='https://ui.shadcn.com/placeholder.svg'
|
||||
alt='placeholder image'
|
||||
width={960}
|
||||
height={540}
|
||||
className='aspect-video w-240 rounded-xl object-cover dark:brightness-[0.95] dark:invert'
|
||||
/>
|
||||
<div className='text-center'>
|
||||
<h1 className='mb-4 text-3xl font-bold'>500</h1>
|
||||
<h2 className="mb-3 text-2xl font-semibold">Internal Server Error</h2>
|
||||
<p>Something went wrong on our end. We're working to fix the issue. Please try again later.</p>
|
||||
<div className='mt-6 flex items-center justify-center gap-4 md:mt-8'>
|
||||
<Button className='cursor-pointer' onClick={() => router.push('/dashboard')}>Go Back Home</Button>
|
||||
<Button variant='outline' className='flex cursor-pointer items-center gap-1' onClick={() => router.push('#')}>
|
||||
Contact Us
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { InternalServerError } from "./components/internal-server-error"
|
||||
|
||||
export default function InternalServerErrorPage() {
|
||||
return <InternalServerError />
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
"use client"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { useRouter } from "next/navigation"
|
||||
import Image from "next/image"
|
||||
|
||||
export function NotFoundError() {
|
||||
const router = useRouter()
|
||||
|
||||
return (
|
||||
<div className='mx-auto flex min-h-dvh flex-col items-center justify-center gap-8 p-8 md:gap-12 md:p-16'>
|
||||
<Image
|
||||
src='https://ui.shadcn.com/placeholder.svg'
|
||||
alt='placeholder image'
|
||||
width={960}
|
||||
height={540}
|
||||
className='aspect-video w-240 rounded-xl object-cover dark:brightness-[0.95] dark:invert'
|
||||
/>
|
||||
<div className='text-center'>
|
||||
<h1 className='mb-4 text-3xl font-bold'>404</h1>
|
||||
<h2 className="mb-3 text-2xl font-semibold">Page Not Found</h2>
|
||||
<p>The page you are looking for doesn't exist or has been moved to another location.</p>
|
||||
<div className='mt-6 flex items-center justify-center gap-4 md:mt-8'>
|
||||
<Button className='cursor-pointer' onClick={() => router.push('/dashboard')}>Go Back Home</Button>
|
||||
<Button variant='outline' className='flex cursor-pointer items-center gap-1' onClick={() => router.push('#')}>
|
||||
Contact Us
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { NotFoundError } from "./components/not-found-error"
|
||||
|
||||
export default function NotFoundPage() {
|
||||
return <NotFoundError />
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
"use client"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { useRouter } from "next/navigation"
|
||||
import Image from "next/image"
|
||||
|
||||
export function UnauthorizedError() {
|
||||
const router = useRouter()
|
||||
|
||||
return (
|
||||
<div className='mx-auto flex min-h-dvh flex-col items-center justify-center gap-8 p-8 md:gap-12 md:p-16'>
|
||||
<Image
|
||||
src='https://ui.shadcn.com/placeholder.svg'
|
||||
alt='placeholder image'
|
||||
width={960}
|
||||
height={540}
|
||||
className='aspect-video w-240 rounded-xl object-cover dark:brightness-[0.95] dark:invert'
|
||||
/>
|
||||
<div className='text-center'>
|
||||
<h1 className='mb-4 text-3xl font-bold'>401</h1>
|
||||
<h2 className="mb-3 text-2xl font-semibold">Unauthorized</h2>
|
||||
<p>You don't have permission to access this resource. Please sign in or contact your administrator.</p>
|
||||
<div className='mt-6 flex items-center justify-center gap-4 md:mt-8'>
|
||||
<Button className='cursor-pointer' onClick={() => router.push('/dashboard')}>Go Back Home</Button>
|
||||
<Button variant='outline' className='flex cursor-pointer items-center gap-1' onClick={() => router.push('#')}>
|
||||
Contact Us
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { UnauthorizedError } from "./components/unauthorized-error"
|
||||
|
||||
export default function UnauthorizedPage() {
|
||||
return <UnauthorizedError />
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
"use client"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { useRouter } from "next/navigation"
|
||||
import Image from "next/image"
|
||||
|
||||
export function UnderMaintenanceError() {
|
||||
const router = useRouter()
|
||||
|
||||
return (
|
||||
<div className='mx-auto flex min-h-dvh flex-col items-center justify-center gap-8 p-8 md:gap-12 md:p-16'>
|
||||
<Image
|
||||
src='https://ui.shadcn.com/placeholder.svg'
|
||||
alt='placeholder image'
|
||||
width={960}
|
||||
height={540}
|
||||
className='aspect-video w-240 rounded-xl object-cover dark:brightness-[0.95] dark:invert'
|
||||
/>
|
||||
<div className='text-center'>
|
||||
<h1 className='mb-4 text-3xl font-bold'>503</h1>
|
||||
<h2 className="mb-3 text-2xl font-semibold">Under Maintenance</h2>
|
||||
<p>The service is currently unavailable. Please try again later.</p>
|
||||
<div className='mt-6 flex items-center justify-center gap-4 md:mt-8'>
|
||||
<Button className='cursor-pointer' onClick={() => router.push('/dashboard')}>Go Back Home</Button>
|
||||
<Button variant='outline' className='flex cursor-pointer items-center gap-1' onClick={() => router.push('#')}>
|
||||
Contact Us
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { UnderMaintenanceError } from "./components/under-maintenance-error"
|
||||
|
||||
export default function UnderMaintenancePage() {
|
||||
return <UnderMaintenanceError />
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
"use client"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Label } from "@/components/ui/label"
|
||||
|
||||
export function ForgotPasswordForm2({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"form">) {
|
||||
return (
|
||||
<form className={cn("flex flex-col gap-6", className)} {...props}>
|
||||
<div className="flex flex-col items-center gap-2 text-center">
|
||||
<h1 className="text-2xl font-bold">Forgot your password?</h1>
|
||||
<p className="text-muted-foreground text-sm text-balance">
|
||||
Enter your email address and we'll send you a link to reset your password
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid gap-6">
|
||||
<div className="grid gap-3">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<Input id="email" type="email" placeholder="m@example.com" required />
|
||||
</div>
|
||||
<Button type="submit" className="w-full cursor-pointer">
|
||||
Send Reset Link
|
||||
</Button>
|
||||
</div>
|
||||
<div className="text-center text-sm">
|
||||
Remember your password?{" "}
|
||||
<a href="/auth/sign-in-2" className="underline underline-offset-4">
|
||||
Back to sign in
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { ForgotPasswordForm2 } from "./components/forgot-password-form-2"
|
||||
import { Logo } from "@/components/logo"
|
||||
import Link from "next/link"
|
||||
import Image from "next/image"
|
||||
|
||||
export default function ForgotPassword2Page() {
|
||||
return (
|
||||
<div className="grid min-h-svh lg:grid-cols-2">
|
||||
<div className="flex flex-col gap-4 p-6 md:p-10">
|
||||
<div className="flex justify-center gap-2 md:justify-start">
|
||||
<Link href="/" className="flex items-center gap-2 font-medium">
|
||||
<div className="bg-primary text-primary-foreground flex size-8 items-center justify-center rounded-md">
|
||||
<Logo size={24} />
|
||||
</div>
|
||||
ShadcnStore
|
||||
</Link>
|
||||
</div>
|
||||
<div className="flex flex-1 items-center justify-center">
|
||||
<div className="w-full max-w-md">
|
||||
<ForgotPasswordForm2 />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-muted relative hidden lg:block">
|
||||
<Image
|
||||
src="https://ui.shadcn.com/placeholder.svg"
|
||||
alt="Image"
|
||||
fill
|
||||
className="object-cover dark:brightness-[0.95] dark:invert"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
"use client"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Card, CardContent } from "@/components/ui/card"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { Logo } from "@/components/logo"
|
||||
import Link from "next/link"
|
||||
import Image from "next/image"
|
||||
|
||||
export function ForgotPasswordForm3({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div className={cn("flex flex-col gap-6", className)} {...props}>
|
||||
<Card className="overflow-hidden p-0">
|
||||
<CardContent className="grid p-0 md:grid-cols-2">
|
||||
<form className="p-6 md:p-8">
|
||||
<div className="flex flex-col gap-6">
|
||||
<div className="flex justify-center mb-2">
|
||||
<Link href="/" className="flex items-center gap-2 font-medium">
|
||||
<div className="bg-primary text-primary-foreground flex size-8 items-center justify-center rounded-md">
|
||||
<Logo size={24} />
|
||||
</div>
|
||||
<span className="text-xl">ShadcnStore</span>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="flex flex-col items-center text-center">
|
||||
<h1 className="text-2xl font-bold">Forgot your password?</h1>
|
||||
<p className="text-muted-foreground text-balance">
|
||||
Enter your email to reset your ShadcnStore account password
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid gap-3">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
placeholder="m@example.com"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<Button type="submit" className="w-full cursor-pointer">
|
||||
Send Reset Link
|
||||
</Button>
|
||||
<div className="text-center text-sm">
|
||||
Remember your password?{" "}
|
||||
<a href="/auth/sign-in-3" className="underline underline-offset-4">
|
||||
Back to sign in
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div className="bg-muted relative hidden md:block">
|
||||
<Image
|
||||
src="https://ui.shadcn.com/placeholder.svg"
|
||||
alt="Image"
|
||||
fill
|
||||
className="object-cover dark:brightness-[0.95] dark:invert"
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<div className="text-muted-foreground *:[a]:hover:text-primary text-center text-xs text-balance *:[a]:underline *:[a]:underline-offset-4">
|
||||
By clicking continue, you agree to our <a href="#">Terms of Service</a>{" "}
|
||||
and <a href="#">Privacy Policy</a>.
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { ForgotPasswordForm3 } from "./components/forgot-password-form-3"
|
||||
|
||||
export default function ForgotPassword3Page() {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-background p-4">
|
||||
<ForgotPasswordForm3 className="w-full max-w-sm md:max-w-4xl" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { useActionState } from "react";
|
||||
import { ArrowLeft, Loader2, MailCheck } from "lucide-react";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { requestPasswordResetAction } from "@/lib/appwrite/password-reset-actions";
|
||||
import { initialAuthState } from "@/lib/appwrite/auth-types";
|
||||
|
||||
export function ForgotPasswordForm1({ className, ...props }: React.ComponentProps<"div">) {
|
||||
const [state, formAction, isPending] = useActionState(requestPasswordResetAction, initialAuthState);
|
||||
|
||||
return (
|
||||
<div className={cn("flex flex-col gap-6", className)} {...props}>
|
||||
<Card>
|
||||
<CardHeader className="text-center">
|
||||
<CardTitle className="text-xl">Şifremi unuttum</CardTitle>
|
||||
<CardDescription>
|
||||
Email adresinizi girin, sıfırlama bağlantısı gönderelim.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{state.ok ? (
|
||||
<div className="flex flex-col items-center gap-3 py-4 text-center">
|
||||
<div className="bg-primary/10 text-primary flex size-12 items-center justify-center rounded-full">
|
||||
<MailCheck className="size-6" />
|
||||
</div>
|
||||
<p className="text-sm">
|
||||
Sıfırlama kodunuz e-posta adresinize gönderildi. Kodu girerek şifrenizi yenileyebilirsiniz.
|
||||
</p>
|
||||
<Link
|
||||
href="/sign-in"
|
||||
className="text-muted-foreground hover:text-foreground mt-2 flex items-center gap-1 text-sm underline-offset-4 hover:underline"
|
||||
>
|
||||
<ArrowLeft className="size-3.5" />
|
||||
Giriş sayfasına dön
|
||||
</Link>
|
||||
</div>
|
||||
) : (
|
||||
<form action={formAction} className="flex flex-col gap-4">
|
||||
<div className="grid gap-3">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<Input
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
placeholder="ornek@firma.com"
|
||||
autoComplete="email"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
{state.error && (
|
||||
<p className="text-destructive text-sm text-center" role="alert">
|
||||
{state.error}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<Button type="submit" className="w-full" disabled={isPending}>
|
||||
{isPending ? (
|
||||
<>
|
||||
<Loader2 className="size-4 animate-spin" />
|
||||
Gönderiliyor...
|
||||
</>
|
||||
) : (
|
||||
"Sıfırlama bağlantısı gönder"
|
||||
)}
|
||||
</Button>
|
||||
|
||||
<Link
|
||||
href="/sign-in"
|
||||
className="text-muted-foreground hover:text-foreground flex items-center justify-center gap-1 text-sm underline-offset-4 hover:underline"
|
||||
>
|
||||
<ArrowLeft className="size-3.5" />
|
||||
Giriş sayfasına dön
|
||||
</Link>
|
||||
</form>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import Link from "next/link";
|
||||
|
||||
import { ForgotPasswordForm1 } from "./components/forgot-password-form-1";
|
||||
import { Logo } from "@/components/logo";
|
||||
|
||||
export default function ForgotPasswordPage() {
|
||||
return (
|
||||
<div className="bg-muted flex min-h-svh flex-col items-center justify-center gap-6 p-6 md:p-10">
|
||||
<div className="flex w-full max-w-sm flex-col gap-6">
|
||||
<Link href="/" className="flex items-center gap-2 self-center font-medium">
|
||||
<div className="bg-primary text-primary-foreground flex size-9 items-center justify-center rounded-md">
|
||||
<Logo size={24} />
|
||||
</div>
|
||||
<span className="text-lg font-semibold">DLS</span>
|
||||
</Link>
|
||||
<ForgotPasswordForm1 />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "DLS — Giriş",
|
||||
description: "DLS hesabınıza giriş yapın veya yeni hesap oluşturun.",
|
||||
};
|
||||
|
||||
export default function AuthLayout({ children }: { children: React.ReactNode }) {
|
||||
return <div className="min-h-screen bg-background">{children}</div>;
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { useActionState } from "react";
|
||||
import { ArrowLeft, Loader2, ShieldCheck } from "lucide-react";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { resetPasswordAction } from "@/lib/appwrite/password-reset-actions";
|
||||
import { initialAuthState } from "@/lib/appwrite/auth-types";
|
||||
|
||||
interface Props extends React.ComponentProps<"div"> {
|
||||
token: string;
|
||||
}
|
||||
|
||||
export function ResetPasswordForm({ token, className, ...props }: Props) {
|
||||
const [state, formAction, isPending] = useActionState(resetPasswordAction, initialAuthState);
|
||||
|
||||
return (
|
||||
<div className={cn("flex flex-col gap-6", className)} {...props}>
|
||||
<Card>
|
||||
<CardHeader className="text-center">
|
||||
<div className="bg-primary/10 text-primary mx-auto mb-2 flex size-12 items-center justify-center rounded-full">
|
||||
<ShieldCheck className="size-6" />
|
||||
</div>
|
||||
<CardTitle className="text-xl">Yeni şifre belirle</CardTitle>
|
||||
<CardDescription>
|
||||
Kod doğrulandı. Yeni şifrenizi girin.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<form action={formAction} className="flex flex-col gap-4">
|
||||
<input type="hidden" name="token" value={token} />
|
||||
|
||||
<div className="grid gap-3">
|
||||
<Label htmlFor="password">Yeni şifre</Label>
|
||||
<Input
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
placeholder="En az 8 karakter"
|
||||
autoComplete="new-password"
|
||||
required
|
||||
minLength={8}
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-3">
|
||||
<Label htmlFor="confirmPassword">Şifre tekrar</Label>
|
||||
<Input
|
||||
id="confirmPassword"
|
||||
name="confirmPassword"
|
||||
type="password"
|
||||
placeholder="Şifreyi tekrar girin"
|
||||
autoComplete="new-password"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
{state.error && (
|
||||
<p className="text-destructive text-center text-sm" role="alert">
|
||||
{state.error}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<Button type="submit" className="w-full" disabled={isPending}>
|
||||
{isPending ? (
|
||||
<>
|
||||
<Loader2 className="size-4 animate-spin" />
|
||||
Güncelleniyor...
|
||||
</>
|
||||
) : (
|
||||
"Şifreyi güncelle"
|
||||
)}
|
||||
</Button>
|
||||
|
||||
<Link
|
||||
href="/sign-in"
|
||||
className="text-muted-foreground hover:text-foreground flex items-center justify-center gap-1 text-sm underline-offset-4 hover:underline"
|
||||
>
|
||||
<ArrowLeft className="size-3.5" />
|
||||
Giriş sayfasına dön
|
||||
</Link>
|
||||
</form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import Link from "next/link";
|
||||
import { XCircle } from "lucide-react";
|
||||
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { verifyResetToken } from "@/lib/appwrite/password-reset-actions";
|
||||
import { ResetPasswordForm } from "./components/reset-password-form";
|
||||
|
||||
interface Props {
|
||||
searchParams: Promise<{ token?: string }>;
|
||||
}
|
||||
|
||||
export default async function ResetPasswordPage({ searchParams }: Props) {
|
||||
const { token } = await searchParams;
|
||||
|
||||
if (!token) {
|
||||
return <InvalidToken message="Geçersiz bağlantı. Yeni bir sıfırlama kodu talep edin." />;
|
||||
}
|
||||
|
||||
const { valid } = await verifyResetToken(token);
|
||||
|
||||
if (!valid) {
|
||||
return <InvalidToken message="Bu kod geçersiz veya süresi dolmuş. Yeni bir sıfırlama kodu talep edin." />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex min-h-svh items-center justify-center p-6">
|
||||
<div className="w-full max-w-sm">
|
||||
<ResetPasswordForm token={token} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function InvalidToken({ message }: { message: string }) {
|
||||
return (
|
||||
<div className="flex min-h-svh items-center justify-center p-6">
|
||||
<div className="w-full max-w-sm">
|
||||
<Card>
|
||||
<CardHeader className="text-center">
|
||||
<div className="text-destructive mx-auto mb-2 flex size-12 items-center justify-center rounded-full bg-red-50">
|
||||
<XCircle className="size-6" />
|
||||
</div>
|
||||
<CardTitle className="text-xl">Geçersiz kod</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-col items-center gap-4 text-center">
|
||||
<p className="text-muted-foreground text-sm">{message}</p>
|
||||
<Link
|
||||
href="/forgot-password"
|
||||
className="text-primary text-sm underline underline-offset-4"
|
||||
>
|
||||
Yeni kod talep et
|
||||
</Link>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
"use client"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Label } from "@/components/ui/label"
|
||||
|
||||
export function LoginForm2({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"form">) {
|
||||
return (
|
||||
<form className={cn("flex flex-col gap-6", className)} {...props} action="/dashboard">
|
||||
<div className="flex flex-col items-center gap-2 text-center">
|
||||
<h1 className="text-2xl font-bold">Login to your account</h1>
|
||||
<p className="text-muted-foreground text-sm text-balance">
|
||||
Enter your email below to login to your account
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid gap-6">
|
||||
<div className="grid gap-3">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<Input id="email" type="email" placeholder="test@example.com" defaultValue="test@example.com" required />
|
||||
</div>
|
||||
<div className="grid gap-3">
|
||||
<div className="flex items-center">
|
||||
<Label htmlFor="password">Password</Label>
|
||||
<a
|
||||
href="/auth/forgot-password-2"
|
||||
className="ml-auto text-sm underline-offset-4 hover:underline"
|
||||
>
|
||||
Forgot your password?
|
||||
</a>
|
||||
</div>
|
||||
<Input id="password" type="password" defaultValue="password" required />
|
||||
</div>
|
||||
<Button type="submit" className="w-full cursor-pointer">
|
||||
Login
|
||||
</Button>
|
||||
<div className="after:border-border relative text-center text-sm after:absolute after:inset-0 after:top-1/2 after:z-0 after:flex after:items-center after:border-t">
|
||||
<span className="bg-background text-muted-foreground relative z-10 px-2">
|
||||
Or continue with
|
||||
</span>
|
||||
</div>
|
||||
<Button variant="outline" className="w-full cursor-pointer">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
Login with GitHub
|
||||
</Button>
|
||||
</div>
|
||||
<div className="text-center text-sm">
|
||||
Don't have an account?{" "}
|
||||
<a href="/auth/sign-up-2" className="underline underline-offset-4">
|
||||
Sign up
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { LoginForm2 } from "./components/login-form-2"
|
||||
import { Logo } from "@/components/logo"
|
||||
import Link from "next/link"
|
||||
import Image from "next/image"
|
||||
|
||||
export default function LoginPage() {
|
||||
return (
|
||||
<div className="grid min-h-svh lg:grid-cols-2">
|
||||
<div className="flex flex-col gap-4 p-6 md:p-10">
|
||||
<div className="flex justify-center gap-2 md:justify-start">
|
||||
<Link href="/" className="flex items-center gap-2 font-medium">
|
||||
<div className="bg-primary text-primary-foreground flex size-8 items-center justify-center rounded-md">
|
||||
<Logo size={24} />
|
||||
</div>
|
||||
ShadcnStore
|
||||
</Link>
|
||||
</div>
|
||||
<div className="flex flex-1 items-center justify-center">
|
||||
<div className="w-full max-w-md">
|
||||
<LoginForm2 />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-muted relative hidden lg:block">
|
||||
<Image
|
||||
src="https://ui.shadcn.com/placeholder.svg"
|
||||
alt="Image"
|
||||
fill
|
||||
className="object-cover dark:brightness-[0.95] dark:invert"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
"use client"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Card, CardContent } from "@/components/ui/card"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { Logo } from "@/components/logo"
|
||||
import Link from "next/link"
|
||||
import Image from "next/image"
|
||||
|
||||
export function LoginForm3({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div className={cn("flex flex-col gap-6", className)} {...props}>
|
||||
<Card className="overflow-hidden p-0">
|
||||
<CardContent className="grid p-0 md:grid-cols-2">
|
||||
<form className="p-6 md:p-8" action="/dashboard">
|
||||
<div className="flex flex-col gap-6">
|
||||
<div className="flex justify-center mb-2">
|
||||
<Link href="/" className="flex items-center gap-2 font-medium">
|
||||
<div className="bg-primary text-primary-foreground flex size-8 items-center justify-center rounded-md">
|
||||
<Logo size={24} />
|
||||
</div>
|
||||
<span className="text-xl">ShadcnStore</span>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="flex flex-col items-center text-center">
|
||||
<h1 className="text-2xl font-bold">Welcome back</h1>
|
||||
<p className="text-muted-foreground text-balance">
|
||||
Login to your ShadcnStore account
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid gap-3">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
placeholder="test@example.com"
|
||||
defaultValue="test@example.com"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-3">
|
||||
<div className="flex items-center">
|
||||
<Label htmlFor="password">Password</Label>
|
||||
<a
|
||||
href="/auth/forgot-password-3"
|
||||
className="ml-auto text-sm underline-offset-2 hover:underline"
|
||||
>
|
||||
Forgot your password?
|
||||
</a>
|
||||
</div>
|
||||
<Input id="password" type="password" defaultValue="password" required />
|
||||
</div>
|
||||
<Button type="submit" className="w-full cursor-pointer">
|
||||
Login
|
||||
</Button>
|
||||
<div className="after:border-border relative text-center text-sm after:absolute after:inset-0 after:top-1/2 after:z-0 after:flex after:items-center after:border-t">
|
||||
<span className="bg-card text-muted-foreground relative z-10 px-2">
|
||||
Or continue with
|
||||
</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
<Button variant="outline" type="button" className="w-full cursor-pointer">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M12.152 6.896c-.948 0-2.415-1.078-3.96-1.04-2.04.027-3.91 1.183-4.961 3.014-2.117 3.675-.546 9.103 1.519 12.09 1.013 1.454 2.208 3.09 3.792 3.039 1.52-.065 2.09-.987 3.935-.987 1.831 0 2.35.987 3.96.948 1.637-.026 2.676-1.48 3.676-2.948 1.156-1.688 1.636-3.325 1.662-3.415-.039-.013-3.182-1.221-3.22-4.857-.026-3.04 2.48-4.494 2.597-4.559-1.429-2.09-3.623-2.324-4.39-2.376-2-.156-3.675 1.09-4.61 1.09zM15.53 3.83c.843-1.012 1.4-2.427 1.245-3.83-1.207.052-2.662.805-3.532 1.818-.78.896-1.454 2.338-1.273 3.714 1.338.104 2.715-.688 3.559-1.701"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
<span className="sr-only">Login with Apple</span>
|
||||
</Button>
|
||||
<Button variant="outline" type="button" className="w-full cursor-pointer">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M12.48 10.92v3.28h7.84c-.24 1.84-.853 3.187-1.787 4.133-1.147 1.147-2.933 2.4-6.053 2.4-4.827 0-8.6-3.893-8.6-8.72s3.773-8.72 8.6-8.72c2.6 0 4.507 1.027 5.907 2.347l2.307-2.307C18.747 1.44 16.133 0 12.48 0 5.867 0 .307 5.387.307 12s5.56 12 12.173 12c3.573 0 6.267-1.173 8.373-3.36 2.16-2.16 2.84-5.213 2.84-7.667 0-.76-.053-1.467-.173-2.053H12.48z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
<span className="sr-only">Login with Google</span>
|
||||
</Button>
|
||||
<Button variant="outline" type="button" className="w-full cursor-pointer">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M6.915 4.03c-1.968 0-3.683 1.28-4.871 3.113C.704 9.208 0 11.883 0 14.449c0 .706.07 1.369.21 1.973a6.624 6.624 0 0 0 .265.86 5.297 5.297 0 0 0 .371.761c.696 1.159 1.818 1.927 3.593 1.927 1.497 0 2.633-.671 3.965-2.444.76-1.012 1.144-1.626 2.663-4.32l.756-1.339.186-.325c.061.1.121.196.183.3l2.152 3.595c.724 1.21 1.665 2.556 2.47 3.314 1.046.987 1.992 1.22 3.06 1.22 1.075 0 1.876-.355 2.455-.843a3.743 3.743 0 0 0 .81-.973c.542-.939.861-2.127.861-3.745 0-2.72-.681-5.357-2.084-7.45-1.282-1.912-2.957-2.93-4.716-2.93-1.047 0-2.088.467-3.053 1.308-.652.57-1.257 1.29-1.82 2.05-.69-.875-1.335-1.547-1.958-2.056-1.182-.966-2.315-1.303-3.454-1.303zm10.16 2.053c1.147 0 2.188.758 2.992 1.999 1.132 1.748 1.647 4.195 1.647 6.4 0 1.548-.368 2.9-1.839 2.9-.58 0-1.027-.23-1.664-1.004-.496-.601-1.343-1.878-2.832-4.358l-.617-1.028a44.908 44.908 0 0 0-1.255-1.98c.07-.109.141-.224.211-.327 1.12-1.667 2.118-2.602 3.358-2.602zm-10.201.553c1.265 0 2.058.791 2.675 1.446.307.327.737.871 1.234 1.579l-1.02 1.566c-.757 1.163-1.882 3.017-2.837 4.338-1.191 1.649-1.81 1.817-2.486 1.817-.524 0-1.038-.237-1.383-.794-.263-.426-.464-1.13-.464-2.046 0-2.221.63-4.535 1.66-6.088.454-.687.964-1.226 1.533-1.533a2.264 2.264 0 0 1 1.088-.285z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
<span className="sr-only">Login with Meta</span>
|
||||
</Button>
|
||||
</div>
|
||||
<div className="text-center text-sm">
|
||||
Don't have an account?{" "}
|
||||
<a href="/auth/sign-up-3" className="underline underline-offset-4">
|
||||
Sign up
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div className="bg-muted relative hidden md:block">
|
||||
<Image
|
||||
src="https://ui.shadcn.com/placeholder.svg"
|
||||
alt="Image"
|
||||
fill
|
||||
className="object-cover dark:brightness-[0.95] dark:invert"
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<div className="text-muted-foreground *:[a]:hover:text-primary text-center text-xs text-balance *:[a]:underline *:[a]:underline-offset-4">
|
||||
By clicking continue, you agree to our <a href="#">Terms of Service</a>{" "}
|
||||
and <a href="#">Privacy Policy</a>.
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { LoginForm3 } from "./components/login-form-3"
|
||||
|
||||
export default function LoginPage() {
|
||||
return (
|
||||
<div className="bg-muted flex min-h-svh flex-col items-center justify-center p-6 md:p-10">
|
||||
<div className="w-full max-w-sm md:max-w-4xl">
|
||||
<LoginForm3 />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { useActionState } from "react";
|
||||
import { Loader2 } from "lucide-react";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Logo } from "@/components/logo";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { signInAction } from "@/lib/appwrite/auth-actions";
|
||||
import { initialAuthState } from "@/lib/appwrite/auth-types";
|
||||
|
||||
export function LoginForm1({
|
||||
className,
|
||||
inviteCode,
|
||||
...props
|
||||
}: React.ComponentProps<"div"> & { inviteCode?: string }) {
|
||||
const [state, formAction, isPending] = useActionState(signInAction, initialAuthState);
|
||||
|
||||
return (
|
||||
<div className={cn("flex flex-col gap-6", className)} {...props}>
|
||||
<Card className="overflow-hidden p-0">
|
||||
<CardContent className="grid p-0 md:grid-cols-2">
|
||||
<form action={formAction} className="p-6 md:p-10">
|
||||
{inviteCode && <input type="hidden" name="inviteCode" value={inviteCode} />}
|
||||
<div className="flex flex-col gap-6">
|
||||
<div className="flex justify-center">
|
||||
<Link href="/" className="flex items-center gap-2 font-medium">
|
||||
<div className="bg-primary text-primary-foreground flex size-9 items-center justify-center rounded-md">
|
||||
<Logo size={22} />
|
||||
</div>
|
||||
<span className="text-xl font-semibold">DLS</span>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{inviteCode && (
|
||||
<p className="text-muted-foreground rounded-md border bg-muted/50 px-3 py-2 text-center text-xs">
|
||||
Davete katılmak için giriş yapın.
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div className="flex flex-col items-center text-center">
|
||||
<h1 className="text-2xl font-bold tracking-tight">Tekrar hoş geldiniz</h1>
|
||||
<p className="text-muted-foreground text-sm text-balance mt-1">
|
||||
Hesabınıza giriş yaparak işletmenizi yönetmeye devam edin
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-3">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<Input
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
placeholder="ornek@firma.com"
|
||||
autoComplete="email"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-3">
|
||||
<div className="flex items-center">
|
||||
<Label htmlFor="password">Şifre</Label>
|
||||
<Link
|
||||
href="/forgot-password"
|
||||
className="ml-auto text-xs text-muted-foreground hover:text-foreground underline-offset-4 hover:underline"
|
||||
>
|
||||
Şifremi unuttum
|
||||
</Link>
|
||||
</div>
|
||||
<Input
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
autoComplete="current-password"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
{state.error && (
|
||||
<p className="text-destructive text-sm text-center" role="alert">
|
||||
{state.error}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<Button type="submit" className="w-full" disabled={isPending}>
|
||||
{isPending ? (
|
||||
<>
|
||||
<Loader2 className="size-4 animate-spin" />
|
||||
Giriş yapılıyor...
|
||||
</>
|
||||
) : (
|
||||
"Giriş yap"
|
||||
)}
|
||||
</Button>
|
||||
|
||||
<div className="text-center text-sm text-muted-foreground">
|
||||
Hesabınız yok mu?{" "}
|
||||
<Link
|
||||
href="/sign-up"
|
||||
className="text-foreground font-medium underline-offset-4 hover:underline"
|
||||
>
|
||||
Hesap oluştur
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<BrandPanel />
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<p className="text-muted-foreground text-center text-xs text-balance">
|
||||
Giriş yaparak{" "}
|
||||
<Link href="#" className="underline-offset-4 hover:underline">
|
||||
Kullanım Şartları
|
||||
</Link>{" "}
|
||||
ve{" "}
|
||||
<Link href="#" className="underline-offset-4 hover:underline">
|
||||
Gizlilik Politikası
|
||||
</Link>
|
||||
'nı kabul etmiş olursunuz.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function BrandPanel() {
|
||||
return (
|
||||
<div className="bg-primary text-primary-foreground relative hidden md:flex md:flex-col md:justify-between overflow-hidden p-10">
|
||||
<div
|
||||
className="absolute inset-0 opacity-30"
|
||||
style={{
|
||||
backgroundImage:
|
||||
"radial-gradient(circle at 20% 20%, rgba(255,255,255,0.4) 0%, transparent 40%), radial-gradient(circle at 80% 80%, rgba(255,255,255,0.25) 0%, transparent 45%)",
|
||||
}}
|
||||
aria-hidden
|
||||
/>
|
||||
<div
|
||||
className="absolute -top-24 -right-24 size-72 rounded-full bg-white/10 blur-3xl"
|
||||
aria-hidden
|
||||
/>
|
||||
<div
|
||||
className="absolute -bottom-32 -left-20 size-80 rounded-full bg-black/10 blur-3xl"
|
||||
aria-hidden
|
||||
/>
|
||||
|
||||
<div className="relative z-10 flex items-center gap-2">
|
||||
<div className="bg-primary-foreground/15 ring-1 ring-primary-foreground/20 backdrop-blur flex size-10 items-center justify-center rounded-md">
|
||||
<Logo size={22} />
|
||||
</div>
|
||||
<span className="text-lg font-medium">DLS</span>
|
||||
</div>
|
||||
|
||||
<div className="relative z-10 flex flex-col gap-3">
|
||||
<h2 className="text-3xl font-semibold leading-tight">
|
||||
Müşteriden faturaya, tek panelden işletmenizi yönetin.
|
||||
</h2>
|
||||
<p className="text-primary-foreground/80 text-sm">
|
||||
Müşteriler, hizmetler, takvim, görevler ve finans — hepsi tek yerde, multi-tenant ve ekibinize özel.
|
||||
</p>
|
||||
<div className="text-primary-foreground/70 mt-4 text-xs">Kovak Yazılım tarafından</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { LoginForm1 } from "./components/login-form-1";
|
||||
import { getCurrentUser } from "@/lib/appwrite/server";
|
||||
|
||||
export default async function Page({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: Promise<{ invite?: string; reset?: string }>;
|
||||
}) {
|
||||
const { invite, reset } = await searchParams;
|
||||
const user = await getCurrentUser();
|
||||
if (user) redirect(invite ? `/d/${invite}` : "/dashboard");
|
||||
|
||||
return (
|
||||
<div className="bg-muted flex min-h-svh flex-col items-center justify-center p-6 md:p-10">
|
||||
<div className="w-full max-w-sm md:max-w-4xl">
|
||||
{reset === "success" && (
|
||||
<p className="mb-4 rounded-lg bg-green-50 px-4 py-3 text-center text-sm text-green-700">
|
||||
Şifreniz güncellendi. Giriş yapabilirsiniz.
|
||||
</p>
|
||||
)}
|
||||
<LoginForm1 inviteCode={invite} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
"use client"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { Checkbox } from "@/components/ui/checkbox"
|
||||
|
||||
export function SignupForm2({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"form">) {
|
||||
return (
|
||||
<form className={cn("flex flex-col gap-6", className)} {...props}>
|
||||
<div className="flex flex-col items-center gap-2 text-center">
|
||||
<h1 className="text-2xl font-bold">Create your account</h1>
|
||||
<p className="text-muted-foreground text-sm text-balance">
|
||||
Enter your information to create a new account
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid gap-6">
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div className="grid gap-3">
|
||||
<Label htmlFor="firstName">First Name</Label>
|
||||
<Input id="firstName" placeholder="John" required />
|
||||
</div>
|
||||
<div className="grid gap-3">
|
||||
<Label htmlFor="lastName">Last Name</Label>
|
||||
<Input id="lastName" placeholder="Doe" required />
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid gap-3">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<Input id="email" type="email" placeholder="m@example.com" required />
|
||||
</div>
|
||||
<div className="grid gap-3">
|
||||
<Label htmlFor="password">Password</Label>
|
||||
<Input id="password" type="password" required />
|
||||
</div>
|
||||
<div className="grid gap-3">
|
||||
<Label htmlFor="confirmPassword">Confirm Password</Label>
|
||||
<Input id="confirmPassword" type="password" required />
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox id="terms" required />
|
||||
<Label htmlFor="terms" className="text-sm">
|
||||
I agree to the{" "}
|
||||
<a href="#" className="underline underline-offset-4 hover:text-primary">
|
||||
Terms of Service
|
||||
</a>{" "}
|
||||
and{" "}
|
||||
<a href="#" className="underline underline-offset-4 hover:text-primary">
|
||||
Privacy Policy
|
||||
</a>
|
||||
</Label>
|
||||
</div>
|
||||
<Button type="submit" className="w-full cursor-pointer">
|
||||
Create Account
|
||||
</Button>
|
||||
<div className="after:border-border relative text-center text-sm after:absolute after:inset-0 after:top-1/2 after:z-0 after:flex after:items-center after:border-t">
|
||||
<span className="bg-background text-muted-foreground relative z-10 px-2">
|
||||
Or continue with
|
||||
</span>
|
||||
</div>
|
||||
<Button variant="outline" className="w-full cursor-pointer">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
Sign up with GitHub
|
||||
</Button>
|
||||
</div>
|
||||
<div className="text-center text-sm">
|
||||
Already have an account?{" "}
|
||||
<a href="/auth/sign-in-2" className="underline underline-offset-4">
|
||||
Sign in
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { SignupForm2 } from "./components/signup-form-2"
|
||||
import { Logo } from "@/components/logo"
|
||||
import Link from "next/link"
|
||||
import Image from "next/image"
|
||||
|
||||
export default function SignUp2Page() {
|
||||
return (
|
||||
<div className="grid min-h-svh lg:grid-cols-2">
|
||||
<div className="flex flex-col gap-4 p-6 md:p-10">
|
||||
<div className="flex justify-center gap-2 md:justify-start">
|
||||
<Link href="/" className="flex items-center gap-2 font-medium">
|
||||
<div className="bg-primary text-primary-foreground flex size-8 items-center justify-center rounded-md">
|
||||
<Logo size={24} />
|
||||
</div>
|
||||
ShadcnStore
|
||||
</Link>
|
||||
</div>
|
||||
<div className="flex flex-1 items-center justify-center">
|
||||
<div className="w-full max-w-md">
|
||||
<SignupForm2 />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-muted relative hidden lg:block">
|
||||
<Image
|
||||
src="https://ui.shadcn.com/placeholder.svg"
|
||||
alt="Image"
|
||||
fill
|
||||
className="object-cover dark:brightness-[0.95] dark:invert"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
"use client"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Card, CardContent } from "@/components/ui/card"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { Checkbox } from "@/components/ui/checkbox"
|
||||
import { Logo } from "@/components/logo"
|
||||
import Link from "next/link"
|
||||
import Image from "next/image"
|
||||
|
||||
export function SignupForm3({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div className={cn("flex flex-col gap-6", className)} {...props}>
|
||||
<Card className="overflow-hidden p-0">
|
||||
<CardContent className="grid p-0 md:grid-cols-2">
|
||||
<form className="p-6 md:p-8">
|
||||
<div className="flex flex-col gap-6">
|
||||
<div className="flex justify-center mb-2">
|
||||
<Link href="/" className="flex items-center gap-2 font-medium">
|
||||
<div className="bg-primary text-primary-foreground flex size-8 items-center justify-center rounded-md">
|
||||
<Logo size={24} />
|
||||
</div>
|
||||
<span className="text-xl">ShadcnStore</span>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="flex flex-col items-center text-center">
|
||||
<h1 className="text-2xl font-bold">Create your account</h1>
|
||||
<p className="text-muted-foreground text-balance">
|
||||
Enter your information to create a new account
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div className="grid gap-3">
|
||||
<Label htmlFor="firstName">First Name</Label>
|
||||
<Input
|
||||
id="firstName"
|
||||
placeholder="John"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-3">
|
||||
<Label htmlFor="lastName">Last Name</Label>
|
||||
<Input
|
||||
id="lastName"
|
||||
placeholder="Doe"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid gap-3">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
placeholder="m@example.com"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-3">
|
||||
<Label htmlFor="password">Password</Label>
|
||||
<Input id="password" type="password" required />
|
||||
</div>
|
||||
<div className="grid gap-3">
|
||||
<Label htmlFor="confirmPassword">Confirm Password</Label>
|
||||
<Input id="confirmPassword" type="password" required />
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox id="terms" required />
|
||||
<Label htmlFor="terms" className="text-sm">
|
||||
I agree to the{" "}
|
||||
<a href="#" className="underline underline-offset-4 hover:text-primary">
|
||||
Terms of Service
|
||||
</a>{" "}
|
||||
and{" "}
|
||||
<a href="#" className="underline underline-offset-4 hover:text-primary">
|
||||
Privacy Policy
|
||||
</a>
|
||||
</Label>
|
||||
</div>
|
||||
<Button type="submit" className="w-full cursor-pointer">
|
||||
Create Account
|
||||
</Button>
|
||||
<div className="after:border-border relative text-center text-sm after:absolute after:inset-0 after:top-1/2 after:z-0 after:flex after:items-center after:border-t">
|
||||
<span className="bg-card text-muted-foreground relative z-10 px-2">
|
||||
Or continue with
|
||||
</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
<Button variant="outline" type="button" className="w-full cursor-pointer">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M12.152 6.896c-.948 0-2.415-1.078-3.96-1.04-2.04.027-3.91 1.183-4.961 3.014-2.117 3.675-.546 9.103 1.519 12.09 1.013 1.454 2.208 3.09 3.792 3.039 1.52-.065 2.09-.987 3.935-.987 1.831 0 2.35.987 3.96.948 1.637-.026 2.676-1.48 3.676-2.948 1.156-1.688 1.636-3.325 1.662-3.415-.039-.013-3.182-1.221-3.22-4.857-.026-3.04 2.48-4.494 2.597-4.559-1.429-2.09-3.623-2.324-4.39-2.376-2-.156-3.675 1.09-4.61 1.09zM15.53 3.83c.843-1.012 1.4-2.427 1.245-3.83-1.207.052-2.662.805-3.532 1.818-.78.896-1.454 2.338-1.273 3.714 1.338.104 2.715-.688 3.559-1.701"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
<span className="sr-only">Sign up with Apple</span>
|
||||
</Button>
|
||||
<Button variant="outline" type="button" className="w-full cursor-pointer">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M12.48 10.92v3.28h7.84c-.24 1.84-.853 3.187-1.787 4.133-1.147 1.147-2.933 2.4-6.053 2.4-4.827 0-8.6-3.893-8.6-8.72s3.773-8.72 8.6-8.72c2.6 0 4.507 1.027 5.907 2.347l2.307-2.307C18.747 1.44 16.133 0 12.48 0 5.867 0 .307 5.387.307 12s5.56 12 12.173 12c3.573 0 6.267-1.173 8.373-3.36 2.16-2.16 2.84-5.213 2.84-7.667 0-.76-.053-1.467-.173-2.053H12.48z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
<span className="sr-only">Sign up with Google</span>
|
||||
</Button>
|
||||
<Button variant="outline" type="button" className="w-full cursor-pointer">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M6.915 4.03c-1.968 0-3.683 1.28-4.871 3.113C.704 9.208 0 11.883 0 14.449c0 .706.07 1.369.21 1.973a6.624 6.624 0 0 0 .265.86 5.297 5.297 0 0 0 .371.761c.696 1.159 1.818 1.927 3.593 1.927 1.497 0 2.633-.671 3.965-2.444.76-1.012 1.144-1.626 2.663-4.32l.756-1.339.186-.325c.061.1.121.196.183.3l2.152 3.595c.724 1.21 1.665 2.556 2.47 3.314 1.046.987 1.992 1.22 3.06 1.22 1.075 0 1.876-.355 2.455-.843a3.743 3.743 0 0 0 .81-.973c.542-.939.861-2.127.861-3.745 0-2.72-.681-5.357-2.084-7.45-1.282-1.912-2.957-2.93-4.716-2.93-1.047 0-2.088.467-3.053 1.308-.652.57-1.257 1.29-1.82 2.05-.69-.875-1.335-1.547-1.958-2.056-1.182-.966-2.315-1.303-3.454-1.303zm10.16 2.053c1.147 0 2.188.758 2.992 1.999 1.132 1.748 1.647 4.195 1.647 6.4 0 1.548-.368 2.9-1.839 2.9-.58 0-1.027-.23-1.664-1.004-.496-.601-1.343-1.878-2.832-4.358l-.617-1.028a44.908 44.908 0 0 0-1.255-1.98c.07-.109.141-.224.211-.327 1.12-1.667 2.118-2.602 3.358-2.602zm-10.201.553c1.265 0 2.058.791 2.675 1.446.307.327.737.871 1.234 1.579l-1.02 1.566c-.757 1.163-1.882 3.017-2.837 4.338-1.191 1.649-1.81 1.817-2.486 1.817-.524 0-1.038-.237-1.383-.794-.263-.426-.464-1.13-.464-2.046 0-2.221.63-4.535 1.66-6.088.454-.687.964-1.226 1.533-1.533a2.264 2.264 0 0 1 1.088-.285z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
<span className="sr-only">Sign up with Meta</span>
|
||||
</Button>
|
||||
</div>
|
||||
<div className="text-center text-sm">
|
||||
Already have an account?{" "}
|
||||
<a href="/auth/sign-in-3" className="underline underline-offset-4">
|
||||
Sign in
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div className="bg-muted relative hidden md:block">
|
||||
<Image
|
||||
src="https://ui.shadcn.com/placeholder.svg"
|
||||
alt="Image"
|
||||
fill
|
||||
className="object-cover dark:brightness-[0.95] dark:invert"
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<div className="text-muted-foreground *:[a]:hover:text-primary text-center text-xs text-balance *:[a]:underline *:[a]:underline-offset-4">
|
||||
By clicking continue, you agree to our <a href="#">Terms of Service</a>{" "}
|
||||
and <a href="#">Privacy Policy</a>.
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { SignupForm3 } from "./components/signup-form-3"
|
||||
|
||||
export default function SignUp3Page() {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-background p-4">
|
||||
<SignupForm3 className="w-full max-w-5xl" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { useActionState } from "react";
|
||||
import { Loader2 } from "lucide-react";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Logo } from "@/components/logo";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { signUpAction } from "@/lib/appwrite/auth-actions";
|
||||
import { initialAuthState } from "@/lib/appwrite/auth-types";
|
||||
|
||||
export function SignupForm1({
|
||||
className,
|
||||
inviteCode,
|
||||
prefilledEmail,
|
||||
...props
|
||||
}: React.ComponentProps<"div"> & { inviteCode?: string; prefilledEmail?: string }) {
|
||||
const [state, formAction, isPending] = useActionState(signUpAction, initialAuthState);
|
||||
|
||||
return (
|
||||
<div className={cn("flex flex-col gap-6", className)} {...props}>
|
||||
<Card className="overflow-hidden p-0">
|
||||
<CardContent className="grid p-0 md:grid-cols-2">
|
||||
<BrandPanel />
|
||||
|
||||
<form action={formAction} className="p-6 md:p-10">
|
||||
{inviteCode && <input type="hidden" name="inviteCode" value={inviteCode} />}
|
||||
<div className="flex flex-col gap-6">
|
||||
<div className="flex justify-center">
|
||||
<Link href="/" className="flex items-center gap-2 font-medium">
|
||||
<div className="bg-primary text-primary-foreground flex size-9 items-center justify-center rounded-md">
|
||||
<Logo size={22} />
|
||||
</div>
|
||||
<span className="text-xl font-semibold">DLS</span>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-center text-center">
|
||||
<h1 className="text-2xl font-bold tracking-tight">
|
||||
{inviteCode ? "Davete katıl" : "Hesap oluşturun"}
|
||||
</h1>
|
||||
<p className="text-muted-foreground text-sm text-balance mt-1">
|
||||
{inviteCode
|
||||
? "Hesap oluşturduktan sonra çalışma alanına otomatik katılacaksınız"
|
||||
: "Birkaç saniye içinde hesabınız hazır"}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-3">
|
||||
<Label htmlFor="name">Adınız Soyadınız</Label>
|
||||
<Input
|
||||
id="name"
|
||||
name="name"
|
||||
type="text"
|
||||
placeholder="Ahmet Yılmaz"
|
||||
autoComplete="name"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-3">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<Input
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
placeholder="ornek@firma.com"
|
||||
autoComplete="email"
|
||||
defaultValue={prefilledEmail}
|
||||
readOnly={Boolean(prefilledEmail)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-3">
|
||||
<Label htmlFor="password">Şifre</Label>
|
||||
<Input
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
autoComplete="new-password"
|
||||
minLength={8}
|
||||
required
|
||||
/>
|
||||
<p className="text-muted-foreground text-xs">En az 8 karakter</p>
|
||||
</div>
|
||||
|
||||
{state.error && (
|
||||
<p className="text-destructive text-sm text-center" role="alert">
|
||||
{state.error}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<Button type="submit" className="w-full" disabled={isPending}>
|
||||
{isPending ? (
|
||||
<>
|
||||
<Loader2 className="size-4 animate-spin" />
|
||||
Hesap oluşturuluyor...
|
||||
</>
|
||||
) : (
|
||||
"Hesap oluştur"
|
||||
)}
|
||||
</Button>
|
||||
|
||||
<div className="text-center text-sm text-muted-foreground">
|
||||
Zaten hesabınız var mı?{" "}
|
||||
<Link
|
||||
href="/sign-in"
|
||||
className="text-foreground font-medium underline-offset-4 hover:underline"
|
||||
>
|
||||
Giriş yap
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<p className="text-muted-foreground text-center text-xs text-balance">
|
||||
Hesap oluşturarak{" "}
|
||||
<Link href="#" className="underline-offset-4 hover:underline">
|
||||
Kullanım Şartları
|
||||
</Link>{" "}
|
||||
ve{" "}
|
||||
<Link href="#" className="underline-offset-4 hover:underline">
|
||||
Gizlilik Politikası
|
||||
</Link>
|
||||
'nı kabul etmiş olursunuz.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function BrandPanel() {
|
||||
return (
|
||||
<div className="bg-primary text-primary-foreground relative hidden md:flex md:flex-col md:justify-between overflow-hidden p-10">
|
||||
<div
|
||||
className="absolute inset-0 opacity-30"
|
||||
style={{
|
||||
backgroundImage:
|
||||
"radial-gradient(circle at 20% 20%, rgba(255,255,255,0.4) 0%, transparent 40%), radial-gradient(circle at 80% 80%, rgba(255,255,255,0.25) 0%, transparent 45%)",
|
||||
}}
|
||||
aria-hidden
|
||||
/>
|
||||
<div
|
||||
className="absolute -top-24 -left-24 size-72 rounded-full bg-white/10 blur-3xl"
|
||||
aria-hidden
|
||||
/>
|
||||
<div
|
||||
className="absolute -bottom-32 -right-20 size-80 rounded-full bg-black/10 blur-3xl"
|
||||
aria-hidden
|
||||
/>
|
||||
|
||||
<div className="relative z-10 flex items-center gap-2">
|
||||
<div className="bg-primary-foreground/15 ring-1 ring-primary-foreground/20 backdrop-blur flex size-10 items-center justify-center rounded-md">
|
||||
<Logo size={22} />
|
||||
</div>
|
||||
<span className="text-lg font-medium">DLS</span>
|
||||
</div>
|
||||
|
||||
<div className="relative z-10 flex flex-col gap-3">
|
||||
<h2 className="text-3xl font-semibold leading-tight">
|
||||
İşletmenizi büyütecek tek araç.
|
||||
</h2>
|
||||
<p className="text-primary-foreground/80 text-sm">
|
||||
Hesap oluşturduktan sonra çalışma alanınızı kuruyor, ekibinizi davet ediyor ve hemen kullanmaya başlıyorsunuz.
|
||||
</p>
|
||||
<ul className="text-primary-foreground/85 mt-2 space-y-1 text-sm">
|
||||
<li>• Müşteri & hizmet yönetimi</li>
|
||||
<li>• Görev ve takvim</li>
|
||||
<li>• Finans ve fatura</li>
|
||||
</ul>
|
||||
<div className="text-primary-foreground/70 mt-4 text-xs">Kovak Yazılım tarafından</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { SignupForm1 } from "./components/signup-form-1";
|
||||
import { getCurrentUser } from "@/lib/appwrite/server";
|
||||
|
||||
export default async function SignUpPage({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: Promise<{ invite?: string; email?: string }>;
|
||||
}) {
|
||||
const { invite, email } = await searchParams;
|
||||
const user = await getCurrentUser();
|
||||
if (user) redirect(invite ? `/d/${invite}` : "/dashboard");
|
||||
|
||||
return (
|
||||
<div className="bg-muted flex min-h-svh flex-col items-center justify-center p-6 md:p-10">
|
||||
<div className="w-full max-w-sm md:max-w-4xl">
|
||||
<SignupForm1 inviteCode={invite} prefilledEmail={email} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user