Files
Reactive-Resume/apps/web/src/features/auth/pages/resume-password.tsx
T
Amruth Pillai f29b92e2fb chore(copy): rewrite marketing, app, and docs copy to read less AI-generated
Rewrites the landing page, in-app microcopy, and public docs, then fixes what the rewrite exposed: stale template counts, a broken quickstart anchor, out-of-sync FAQ structured data, dead error-hint branches in the MCP tools, and wrong-sense translations across all 53 locales. Adds GLOSSARY.md so translators get the right sense of the ambiguous UI terms.
2026-08-28 22:18:29 +02:00

152 lines
4.6 KiB
TypeScript

import { t } from "@lingui/core/macro";
import { Trans } from "@lingui/react/macro";
import { ORPCError } from "@orpc/client";
import { EyeIcon, EyeSlashIcon, LockOpenIcon } from "@phosphor-icons/react";
import { useMutation } from "@tanstack/react-query";
import { useNavigate } from "@tanstack/react-router";
import { useToggle } from "usehooks-ts";
import z from "zod";
import { Button } from "@reactive-resume/ui/components/button";
import { FormControl, FormItem, FormLabel, FormMessage } from "@reactive-resume/ui/components/form";
import { Input } from "@reactive-resume/ui/components/input";
import { toast } from "@reactive-resume/ui/components/toast";
import { getReadableErrorMessage } from "@/libs/error-message";
import { orpc } from "@/libs/orpc/client";
import { useAppForm } from "@/libs/tanstack-form";
const formSchema = z.object({
password: z.string().min(6).max(64),
});
type Props = {
redirectPath: string;
};
export function ResumePasswordPage({ redirectPath }: Props) {
const navigate = useNavigate();
const [showPassword, toggleShowPassword] = useToggle(false);
const { mutate: verifyPassword } = useMutation(orpc.resume.verifyPassword.mutationOptions());
const [username, slug] = redirectPath.split("/").slice(1) as [string, string];
if (!username || !slug) throw navigate({ to: "/" });
const form = useAppForm({
defaultValues: { password: "" },
validators: { onSubmit: formSchema },
onSubmit: ({ value, formApi }) => {
const toastId = toast.add({ type: "loading", description: t`Verifying password...` });
verifyPassword(
{ username, slug, password: value.password },
{
onSuccess: () => {
toast.close(toastId);
void navigate({ to: redirectPath, replace: true });
},
onError: (error) => {
if (error instanceof ORPCError && error.code === "INVALID_PASSWORD") {
toast.close(toastId);
formApi.setFieldMeta("password", (meta) => ({
...meta,
isTouched: true,
errors: [{ message: t`The password you entered is incorrect` }],
errorMap: {
...meta.errorMap,
onSubmit: { message: t`The password you entered is incorrect` },
},
}));
} else {
toast.add({
type: "error",
description: getReadableErrorMessage(
error,
t({
comment: "Fallback toast when resume password verification fails unexpectedly",
message: "Failed to verify the password. Please try again.",
}),
),
id: toastId,
});
}
},
},
);
},
});
return (
<>
<div className="space-y-4 text-center">
<h1 className="font-semibold text-2xl tracking-tight">
<Trans>This resume is password protected</Trans>
</h1>
<div className="text-muted-foreground leading-relaxed">
<Trans>Enter the password the resume owner shared with you.</Trans>
</div>
</div>
<form
className="space-y-6"
onSubmit={(event) => {
event.preventDefault();
event.stopPropagation();
void form.handleSubmit();
}}
>
<form.Field name="password">
{(field) => (
<FormItem hasError={field.state.meta.isTouched && field.state.meta.errors.length > 0}>
<FormLabel>
<Trans comment="Label for password input on protected resume access form">Password</Trans>
</FormLabel>
<div className="flex items-center gap-x-1.5">
<FormControl
render={
<Input
min={6}
max={64}
type={showPassword ? "text" : "password"}
autoComplete="new-password"
name={field.name}
value={field.state.value}
onBlur={field.handleBlur}
onChange={(event) => field.handleChange(event.target.value)}
/>
}
/>
<Button
size="icon"
variant="ghost"
onClick={toggleShowPassword}
aria-label={
showPassword
? t({
comment: "Accessible label for button that hides password on protected resume screen",
message: "Hide password",
})
: t({
comment: "Accessible label for button that reveals password on protected resume screen",
message: "Show password",
})
}
>
{showPassword ? <EyeIcon /> : <EyeSlashIcon />}
</Button>
</div>
<FormMessage errors={field.state.meta.errors} />
</FormItem>
)}
</form.Field>
<Button type="submit" className="w-full">
<LockOpenIcon />
<Trans comment="Primary action button label to unlock a password-protected resume">Unlock</Trans>
</Button>
</form>
</>
);
}