import { Alert, Button, Loader, Modal, Radio, Stack, Text } from "@mantine/core"; import { useMutation, useQuery } from "@tanstack/react-query"; import { KeyRound } from "lucide-react"; import { useState } from "react"; import { useAuth } from "@/auth/useAuth"; import { useToast } from "@/hooks/use-toast"; import { FREIGHT_PERMS, hasPermission } from "@/lib/permissions"; import { api } from "@/services/api"; import type { Company, ResetChannel } from "@/types/customer"; export interface ResetPasswordActionProps { company: Pick; } /** * Staff-triggered password reset. Sends a single-use link to the customer's * primary contact; the customer opens it and picks their own new password. No * credential is ever shown to or handled by staff. */ export default function ResetPasswordAction({ company, }: ResetPasswordActionProps) { const { user } = useAuth(); const { toast } = useToast(); const [opened, setOpened] = useState(false); const [channel, setChannel] = useState("phone"); const allowed = hasPermission(user, FREIGHT_PERMS.customers.resetPassword); // The destination is the primary contact's IAM account, not the company // record — those are different fields and routinely hold different values, so // showing `company.phone` here would tell staff the wrong number. Only fetched // once the modal is open. const targetQuery = useQuery( api.customers.resetTarget.queryOptions({ input: { companyId: company.id }, enabled: allowed && opened, }), ); const target = targetQuery.data; const { mutate, isPending } = useMutation( api.customers.resetPassword.mutationOptions({ onSuccess: (result) => { setOpened(false); toast({ title: "Reset link sent", description: `The customer can set a new password using the link sent to ${result.maskedTarget}. It expires in 24 hours.`, }); }, onError: (error) => { toast({ title: "Could not send reset link", description: error.message, variant: "destructive", }); }, }), ); if (!allowed) return null; // SMS is domestic-only: a foreign number counts as unavailable, same as a // missing one, so staff can't send a link that will never arrive. const phoneUsable = !!target?.phone && target.phoneIsDomestic !== false; const channelMissing = !!target && (channel === "email" ? !target.email : !phoneUsable); return ( <> setOpened(false)} title="Send a password-reset link" centered > We'll send a single-use link to this customer's primary contact. They choose their own new password — you will not see it. The link expires in 24 hours. {targetQuery.isLoading ? ( ) : targetQuery.isError ? ( {targetQuery.error.message} ) : target ? ( <> setChannel(v as ResetChannel)} label={`Send the link to ${target.name || "the primary contact"} via`} > These are the primary contact's own login details, which may differ from the company contact details on the profile. ) : null} ); }