mirror of
https://github.com/xCyanGrizzly/DragonsStash.git
synced 2026-05-10 22:01:16 +00:00
- Auto-extract preview images from ZIP/RAR/7z archives during ingestion - Upload custom preview images via package drawer - Select preview from archive contents with on-demand extraction UI - Manually add Telegram channels by t.me link, username, or invite link - Invite code UX: bulk create, copy link, usage tracking, delete confirm - Incomplete upload recovery: verify dest messages on worker startup - Rebuild package DB by scanning destination channel with live progress Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
202 lines
6.2 KiB
TypeScript
202 lines
6.2 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useTransition } from "react";
|
|
import { useRouter, useSearchParams } from "next/navigation";
|
|
import Link from "next/link";
|
|
import { useForm } from "react-hook-form";
|
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
import { Flame } from "lucide-react";
|
|
import { registerSchema, type RegisterInput } from "@/schemas/auth.schema";
|
|
import { registerUser } from "./actions";
|
|
import { loginAction } from "../login/actions";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Input } from "@/components/ui/input";
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import {
|
|
Form,
|
|
FormControl,
|
|
FormField,
|
|
FormItem,
|
|
FormLabel,
|
|
FormMessage,
|
|
} from "@/components/ui/form";
|
|
import { APP_NAME } from "@/lib/constants";
|
|
|
|
export default function RegisterPage() {
|
|
const router = useRouter();
|
|
const searchParams = useSearchParams();
|
|
const [error, setError] = useState<string | null>(null);
|
|
const [isPending, startTransition] = useTransition();
|
|
|
|
const form = useForm<RegisterInput>({
|
|
resolver: zodResolver(registerSchema),
|
|
defaultValues: {
|
|
name: "",
|
|
email: "",
|
|
password: "",
|
|
confirmPassword: "",
|
|
inviteCode: searchParams.get("code") ?? "",
|
|
},
|
|
});
|
|
|
|
function onSubmit(values: RegisterInput) {
|
|
setError(null);
|
|
startTransition(async () => {
|
|
const result = await registerUser(values);
|
|
|
|
if (!result.success) {
|
|
setError(result.error);
|
|
return;
|
|
}
|
|
|
|
// Auto-login after registration using server action
|
|
try {
|
|
const loginResult = await loginAction({
|
|
email: values.email,
|
|
password: values.password,
|
|
});
|
|
|
|
if (loginResult?.error) {
|
|
setError("Account created but sign in failed. Please try logging in.");
|
|
return;
|
|
}
|
|
|
|
router.push("/dashboard");
|
|
router.refresh();
|
|
} catch {
|
|
// Redirect from server action
|
|
router.push("/dashboard");
|
|
router.refresh();
|
|
}
|
|
});
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<div className="flex flex-col items-center gap-2 text-center">
|
|
<Flame className="h-10 w-10 text-primary" />
|
|
<h1 className="text-2xl font-bold tracking-tight">{APP_NAME}</h1>
|
|
<p className="text-sm text-muted-foreground">Create an account to get started</p>
|
|
</div>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Create Account</CardTitle>
|
|
<CardDescription>You need an invite code to register</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<Form {...form}>
|
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
|
|
{error && (
|
|
<div className="rounded-md bg-destructive/10 p-3 text-sm text-destructive">
|
|
{error}
|
|
</div>
|
|
)}
|
|
|
|
<FormField
|
|
control={form.control}
|
|
name="inviteCode"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel>Invite Code</FormLabel>
|
|
<FormControl>
|
|
<Input
|
|
placeholder="Enter your invite code"
|
|
autoComplete="off"
|
|
{...field}
|
|
/>
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
|
|
<FormField
|
|
control={form.control}
|
|
name="name"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel>Name</FormLabel>
|
|
<FormControl>
|
|
<Input placeholder="Your name" autoComplete="name" {...field} />
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
|
|
<FormField
|
|
control={form.control}
|
|
name="email"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel>Email</FormLabel>
|
|
<FormControl>
|
|
<Input
|
|
type="email"
|
|
placeholder="you@example.com"
|
|
autoComplete="email"
|
|
{...field}
|
|
/>
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
|
|
<FormField
|
|
control={form.control}
|
|
name="password"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel>Password</FormLabel>
|
|
<FormControl>
|
|
<Input
|
|
type="password"
|
|
placeholder="At least 6 characters"
|
|
autoComplete="new-password"
|
|
{...field}
|
|
/>
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
|
|
<FormField
|
|
control={form.control}
|
|
name="confirmPassword"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel>Confirm Password</FormLabel>
|
|
<FormControl>
|
|
<Input
|
|
type="password"
|
|
placeholder="Repeat your password"
|
|
autoComplete="new-password"
|
|
{...field}
|
|
/>
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
|
|
<Button type="submit" className="w-full" disabled={isPending}>
|
|
{isPending ? "Creating account..." : "Create Account"}
|
|
</Button>
|
|
</form>
|
|
</Form>
|
|
|
|
<p className="mt-4 text-center text-sm text-muted-foreground">
|
|
Already have an account?{" "}
|
|
<Link href="/login" className="text-primary underline-offset-4 hover:underline">
|
|
Sign in
|
|
</Link>
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
</>
|
|
);
|
|
}
|