"use client"; import { useSession } from "next-auth/react"; import { Dispatch, FormEvent, SetStateAction, useActionState, useEffect, useState, } from "react"; import { deleteTenant, deleteTenantThenSignOut, switchThenDeleteTenant, } from "@/actions/users/tenants"; import { useToast } from "@/components/shadcn"; import { FormButtons } from "@/components/shadcn/form"; import { Input } from "@/components/shadcn/input/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/shadcn/select/select"; import { reloadPage } from "@/lib/navigation"; import { TenantOption } from "@/types/users"; interface DeleteTenantFormProps { tenantId: string; tenantName: string; isActiveTenant: boolean; isLastTenant: boolean; availableTenants: TenantOption[]; setIsOpen: Dispatch>; } export const DeleteTenantForm = ({ tenantId, tenantName, isActiveTenant, isLastTenant, availableTenants, setIsOpen, }: DeleteTenantFormProps) => { const [deleteState, deleteFormAction] = useActionState(deleteTenant, null); const { update } = useSession(); const { toast } = useToast(); const [confirmName, setConfirmName] = useState(""); const [targetTenantId, setTargetTenantId] = useState(""); const [isSubmitting, setIsSubmitting] = useState(false); const nameMatches = confirmName === tenantName; // Deleting the last tenant needs no switch target — there is nothing to // switch to; the session is closed after deletion instead. const needsSwitchTarget = isActiveTenant && !isLastTenant; const canSubmit = needsSwitchTarget ? nameMatches && targetTenantId !== "" : nameMatches; useEffect(() => { if (!deleteState) return; if ("success" in deleteState) { toast({ title: "Organization deleted", description: "The organization has been permanently deleted.", }); setIsOpen(false); } else { toast({ variant: "destructive", title: "Oops! Something went wrong", description: deleteState.error, }); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [deleteState]); // Handle last-tenant delete: a single server action deletes the tenant and // closes the session, since the API also removes users whose only tenant // was the deleted one. On success it redirects to /sign-in server-side. const handleLastTenantDelete = async (e: FormEvent) => { e.preventDefault(); setIsSubmitting(true); const formData = new FormData(e.currentTarget); try { const result = await deleteTenantThenSignOut(null, formData); if (result && "error" in result) { toast({ variant: "destructive", title: "Oops! Something went wrong", description: result.error, }); setIsSubmitting(false); } } catch (error) { // The action redirects by throwing NEXT_REDIRECT — never a failure. if ( error && typeof error === "object" && "digest" in error && String(error.digest).startsWith("NEXT_REDIRECT") ) { return; } toast({ variant: "destructive", title: "Oops! Something went wrong", description: "The organization could not be deleted. Please try again.", }); setIsSubmitting(false); } }; // Handle active-tenant delete: call server action directly to avoid // React's RSC reconciliation unmounting this component before we can // update the session with the new tokens. const handleActiveTenantDelete = async (e: FormEvent) => { e.preventDefault(); setIsSubmitting(true); const formData = new FormData(e.currentTarget); const result = await switchThenDeleteTenant(null, formData); if ("success" in result) { await update({ accessToken: result.accessToken, refreshToken: result.refreshToken, }); toast({ title: "Organization deleted", description: "Switching to another organization.", }); reloadPage(); } else if (result.accessToken) { // Partial success: switch OK but delete failed — still update session await update({ accessToken: result.accessToken, refreshToken: result.refreshToken, }); toast({ variant: "destructive", title: "Switch succeeded but delete failed", description: result.error, }); reloadPage(); } else { toast({ variant: "destructive", title: "Oops! Something went wrong", description: result.error, }); setIsSubmitting(false); } }; return (
{needsSwitchTarget && targetTenantId && ( )}
Type {tenantName} to confirm deletion:
setConfirmName(e.target.value)} placeholder={tenantName} aria-label={`Type ${tenantName} to confirm deletion`} autoComplete="off" /> {isLastTenant && (
This is your only organization. Deleting it will also remove your user account and close your session.
)} {needsSwitchTarget && (
This is your active organization. Select which organization to switch to after deletion:
)} ); };