import React, { useEffect, useRef, useState } from "react";
import { EyeIcon, EyeOffIcon, Trash2Icon } from "lucide-react";
import { Button } from "./button";
import { Field, FieldError, FieldLabel } from "./field";
import { Input } from "./input";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "./select";
import {
PASSWORD_STRENGTH_RULES,
PasswordStrengthTooltip,
} from "./password-strength-tooltip";
// ---------------------------------------------------------------------------
// SectionHeading
// ---------------------------------------------------------------------------
export interface SectionHeadingProps {
children: React.ReactNode;
}
export function SectionHeading({ children }: SectionHeadingProps) {
return
{children}
;
}
// ---------------------------------------------------------------------------
// FormField — label + required marker wrapper
// ---------------------------------------------------------------------------
export interface FormFieldProps {
label: string;
required?: boolean;
children: React.ReactNode;
}
export function FormField({ label, required, children }: FormFieldProps) {
return (
{label}
{required && *}
{children}
);
}
// ---------------------------------------------------------------------------
// PasswordField — input with show/hide toggle + optional strength popover
// ---------------------------------------------------------------------------
export interface PasswordFieldProps {
label: string;
placeholder: string;
required?: boolean;
showStrengthPopover?: boolean;
onValueChange?: (v: string) => void;
}
export function PasswordField({
label,
placeholder,
required,
showStrengthPopover,
onValueChange,
}: PasswordFieldProps) {
const [show, setShow] = useState(false);
const [error, setError] = useState("");
const [touched, setTouched] = useState(false);
const [value, setValue] = useState("");
const [dismissed, setDismissed] = useState(false);
const wrapperRef = useRef(null);
const allRulesPass = PASSWORD_STRENGTH_RULES.every((r) => r.test(value));
const isPopoverOpen =
!!showStrengthPopover && !dismissed && value.length > 0 && !allRulesPass;
const validate = (v: string) => {
if (!v) return "";
if (v.length < 8) return "Min. 8 characters required";
if (!PASSWORD_STRENGTH_RULES.every((r) => r.test(v)))
return "Password doesn't meet all requirements";
return "";
};
useEffect(() => {
if (!isPopoverOpen) return;
const onMouseDown = (e: MouseEvent) => {
if (!wrapperRef.current?.contains(e.target as Node)) setDismissed(true);
};
document.addEventListener("mousedown", onMouseDown, true);
return () => document.removeEventListener("mousedown", onMouseDown, true);
}, [isPopoverOpen]);
const inputEl = (
{
const v = e.target.value;
setValue(v);
onValueChange?.(v);
if (touched) setError(validate(v));
setDismissed(false);
}}
onFocus={() => setDismissed(false)}
onKeyDown={(e: React.KeyboardEvent) => {
if (e.key === "Tab") {
setTouched(true);
setError(validate(value));
setDismissed(true);
}
}}
onBlur={() => {
setTouched(true);
setError(validate(value));
}}
/>
);
return (
{label}
{required && *}
{showStrengthPopover ? (
{inputEl}
) : (
inputEl
)}
{touched && error && !isPopoverOpen && {error}}
);
}
// ---------------------------------------------------------------------------
// StaffRowItem — email + role select row inside InviteStaffView
// ---------------------------------------------------------------------------
export interface StaffMember {
id: string;
email: string;
role: string;
}
export interface StaffRowItemProps {
member: StaffMember;
onRemove: (id: string) => void;
onChange: (id: string, field: "email" | "role", value: string) => void;
showRemove: boolean;
roleOptions?: string[];
}
const DEFAULT_ROLE_OPTIONS = ["Admin", "Broker", "Support"];
export function StaffRowItem({
member,
onRemove,
onChange,
showRemove,
roleOptions = DEFAULT_ROLE_OPTIONS,
}: StaffRowItemProps) {
return (
onChange(member.id, "email", e.target.value)}
/>
{showRemove && (
)}
);
}