Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 53 additions & 22 deletions components/auth/onboarding-form.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";

import { useUser } from "@clerk/nextjs";
import { AlertCircle } from "lucide-react";
import { AlertCircle, Check, X } from "lucide-react";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { finishOnboarding } from "@/app/actions/_userActions";
Expand All @@ -16,34 +16,55 @@
} from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { cryptoService } from "@/lib/crypto";

Check failure on line 19 in components/auth/onboarding-form.tsx

View workflow job for this annotation

GitHub Actions / Checks

File '/home/runner/work/PasswordSafe/PasswordSafe/lib/crypto.ts' is not a module.
import { isErrorResponse, getErrorInfo } from "@/lib/query-utils";
import { cn } from "@/lib/utils";

const passwordSchema = z

Check failure on line 23 in components/auth/onboarding-form.tsx

View workflow job for this annotation

GitHub Actions / Checks

Cannot find name 'z'.
.object({
password: z

Check failure on line 25 in components/auth/onboarding-form.tsx

View workflow job for this annotation

GitHub Actions / Checks

Cannot find name 'z'.
.string()
.min(12, "At least 12 characters")
.regex(/[A-Z]/, "At least 1 uppercase letter (A–Z)")
.regex(/[a-z]/, "At least 1 lowercase letter (a–z)")
.regex(/\d/, "At least 1 number (0–9)")
.regex(
/[!@#$%^&*()_+\-]/,
"At least 1 special character (!@#$%^&*()_+-)"
),
repeatPassword: z.string(),

Check failure on line 35 in components/auth/onboarding-form.tsx

View workflow job for this annotation

GitHub Actions / Checks

Cannot find name 'z'.
})
.refine(data => data.password === data.repeatPassword, {

Check failure on line 37 in components/auth/onboarding-form.tsx

View workflow job for this annotation

GitHub Actions / Checks

Parameter 'data' implicitly has an 'any' type.
message: "Passwords do not match",
path: ["repeatPassword"],
});

type PasswordFormValues = z.infer<typeof passwordSchema>;

Check failure on line 42 in components/auth/onboarding-form.tsx

View workflow job for this annotation

GitHub Actions / Checks

Cannot find namespace 'z'.

export function SignUpForm({
className,
...props
}: React.ComponentPropsWithoutRef<"div">) {
const [password, setPassword] = useState("");
const [repeatPassword, setRepeatPassword] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string>("");
const router = useRouter();
const { user } = useUser();

const handleSignUp = async (e: React.FormEvent) => {
e.preventDefault();
const {
register,
handleSubmit,
formState: { errors, isValid },
} = useForm<PasswordFormValues>({

Check failure on line 57 in components/auth/onboarding-form.tsx

View workflow job for this annotation

GitHub Actions / Checks

Cannot find name 'useForm'.
resolver: zodResolver(passwordSchema),

Check failure on line 58 in components/auth/onboarding-form.tsx

View workflow job for this annotation

GitHub Actions / Checks

Cannot find name 'zodResolver'.
mode: "onChange",
});

const onSubmit = async (data: PasswordFormValues) => {
setError("");
setIsLoading(true);
if (password !== repeatPassword) {
setError("Passwords do not match");
setIsLoading(false);
return;
}

try {
const { publicKey, wrappedPrivateKey, salt, wrappedDefaultVaultKey } =
await cryptoService.onboarding(password);
await cryptoService.onboarding(data.password);

const response = await finishOnboarding({
salt,
Expand All @@ -52,22 +73,18 @@
wrappedDefaultVaultKey,
});

// Handle error responses
if (isErrorResponse(response)) {
const { error } = response;
console.error(`[${error.code}] Onboarding failed: ${error.message}`);

const errorMessage =
error.code === "ONBOARDING_FAILED"
? "Failed to complete onboarding. Please try again."
: error.code === "UNAUTHORIZED"
? "Authentication failed. Please sign in again."
: error.message;

setError(errorMessage);
return;
}

await user?.reload();
router.push("/");
} catch (error) {
Expand Down Expand Up @@ -105,7 +122,7 @@
</CardDescription>
</CardHeader>
<CardContent>
<form onSubmit={handleSignUp} className="space-y-4">
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
{error && (
<div
className="rounded-md bg-red-50 p-3 text-sm text-red-500"
Expand All @@ -128,9 +145,13 @@
id="password"
type="password"
required
value={password}
onChange={e => setPassword(e.target.value)}
{...register("password")}
/>
{errors.password && (
<div className="pt-1 text-xs text-red-600">
{errors.password.message as string}
</div>
)}
</div>
<div className="grid gap-2">
<div className="flex items-center">
Expand All @@ -142,11 +163,21 @@
id="repeat-password"
type="password"
required
value={repeatPassword}
onChange={e => setRepeatPassword(e.target.value)}
{...register("repeatPassword")}
/>
{errors.repeatPassword && (
<div className="text-xs text-red-600 pt-1">
{errors.repeatPassword.message as string}
</div>
)}
</div>
<Button type="submit" className="w-full" disabled={isLoading}>
<Button
type="submit"
className="w-full"
disabled={
isLoading || !isValid
}
>
{isLoading
? "Setting master password..."
: "Set master password"}
Expand Down
2 changes: 0 additions & 2 deletions lib/crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,5 +311,3 @@ const BufferTransformer = {
return btoa(binary);
},
};

export const cryptoService = new CryptoService();
46 changes: 41 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
},
"dependencies": {
"@clerk/nextjs": "^6.19.4",
"@hookform/resolvers": "^5.1.1",
"@prisma/client": "^6.7.0",
"@radix-ui/react-avatar": "^1.1.9",
"@radix-ui/react-dialog": "^1.1.13",
Expand All @@ -36,8 +37,10 @@
"next-themes": "^0.4.6",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-hook-form": "^7.60.0",
"sonner": "^2.0.3",
"tailwind-merge": "^3.3.0"
"tailwind-merge": "^3.3.0",
"zod": "^3.25.75"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
Expand Down
Loading