Files
claudemesh/apps/web/src/modules/user/settings/security/require-password.tsx
Alejandro Gutiérrez d3163a5bff feat(db): mesh data model — meshes, members, invites, audit log
- pgSchema "mesh" with 4 tables isolating the peer mesh domain
- Enums: visibility, transport, tier, role
- audit_log is metadata-only (E2E encryption enforced at broker/client)
- Cascade on mesh delete, soft-delete via archivedAt/revokedAt

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 21:19:32 +01:00

167 lines
4.5 KiB
TypeScript

"use client";
import { standardSchemaResolver } from "@hookform/resolvers/standard-schema";
import { memo, useCallback, useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { passwordSchema } from "@turbostarter/auth";
import { useTranslation } from "@turbostarter/i18n";
import { Button } from "@turbostarter/ui-web/button";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@turbostarter/ui-web/form";
import { Icons } from "@turbostarter/ui-web/icons";
import { PasswordInput } from "@turbostarter/ui-web/input";
import {
Modal,
ModalClose,
ModalContent,
ModalDescription,
ModalFooter,
ModalHeader,
ModalTitle,
ModalTrigger,
} from "@turbostarter/ui-web/modal";
import { onPromise } from "~/utils";
import type { PasswordPayload } from "@turbostarter/auth";
import type { ComponentProps } from "react";
import type { UseFormReturn } from "react-hook-form";
interface PasswordFormProps {
readonly form: UseFormReturn<PasswordPayload>;
readonly onSubmit: (data: PasswordPayload) => Promise<void>;
readonly children: React.ReactNode;
}
const PasswordForm = memo<PasswordFormProps>(({ form, onSubmit, children }) => {
const { t } = useTranslation(["common", "auth"]);
return (
<Form {...form}>
<form
onSubmit={onPromise(form.handleSubmit(onSubmit))}
className="space-y-4"
>
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem className="w-full space-y-1 px-6 md:px-0">
<FormLabel>{t("password")}</FormLabel>
<FormControl>
<PasswordInput
{...field}
autoComplete="current-password"
disabled={form.formState.isSubmitting}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{children}
</form>
</Form>
);
});
PasswordForm.displayName = "PasswordForm";
interface RequirePasswordProps extends ComponentProps<typeof Modal> {
readonly title?: string;
readonly description?: string;
readonly cta?: string;
readonly onConfirm: (data: PasswordPayload) => Promise<void>;
}
export const RequirePassword = memo<RequirePasswordProps>(
({
title,
description,
onConfirm,
cta,
children,
open: _open,
onOpenChange: _onOpenChange,
...props
}) => {
const [open, setOpen] = useState(_open ?? false);
const { t } = useTranslation(["common", "auth"]);
const form = useForm({
resolver: standardSchemaResolver(passwordSchema),
defaultValues: {
password: "",
},
});
const onSubmit = async (data: PasswordPayload) => {
try {
if (document.activeElement && "blur" in document.activeElement) {
(document.activeElement as HTMLElement).blur();
}
await onConfirm(data);
form.reset();
setOpen(false);
} catch (error) {
setTimeout(() => form.setFocus("password"), 0);
throw error;
}
};
const onOpenChange = useCallback(
(open: boolean) => {
setOpen(open);
_onOpenChange?.(open);
},
[_onOpenChange, setOpen],
);
useEffect(() => {
setOpen(_open ?? false);
}, [_open]);
return (
<Modal {...props} open={open} onOpenChange={onOpenChange}>
<ModalTrigger asChild>{children}</ModalTrigger>
<ModalContent>
<ModalHeader>
<ModalTitle>
{title ?? t("account.password.require.title")}
</ModalTitle>
<ModalDescription className="whitespace-pre-line">
{description ?? t("account.password.require.description")}
</ModalDescription>
</ModalHeader>
<PasswordForm form={form} onSubmit={onSubmit}>
<ModalFooter>
<ModalClose asChild>
<Button variant="outline" type="button">
{t("cancel")}
</Button>
</ModalClose>
<Button type="submit" disabled={form.formState.isSubmitting}>
{form.formState.isSubmitting ? (
<Icons.Loader2 className="size-4 animate-spin" />
) : (
(cta ?? t("continue"))
)}
</Button>
</ModalFooter>
</PasswordForm>
</ModalContent>
</Modal>
);
},
);
RequirePassword.displayName = "RequirePassword";