"use client"; import { useCallback, useEffect, useMemo, useState, type ClipboardEvent, } from "react"; import { useSearchParams } from "next/navigation"; import Button from "../../../../components/buttons/Button"; import InlineTextButton from "../../../../components/buttons/InlineTextButton"; import TextInput from "../../../../components/controls/TextInput"; import Icon from "../../../../components/asset/icon"; import { ASSETS, getAssetPath } from "../../../../../lib/assetUtils"; import { useTranslation } from "../../../../contexts/MessagesContext"; import { fetchAuthSession } from "../../../../../lib/create/api"; import { membershipInvitePreview } from "../../../../../lib/create/membershipInvitePreview"; import { INVITE_EMAIL_PATTERN, formatInviteCountWord, parseInvitePaste, } from "../../../../../lib/create/parseInviteEmails"; import { MAX_STAKEHOLDER_EMAILS } from "../../../../../lib/create/stakeholderLimits"; import { useCreateFlow } from "../../context/CreateFlowContext"; import { MembershipInvitePreviewCard } from "../../components/MembershipInvitePreviewCard"; import { CreateFlowHeaderLockup } from "../../components/CreateFlowHeaderLockup"; import { CreateFlowTwoColumnSelectShell } from "../../components/CreateFlowTwoColumnSelectShell"; import { CREATE_FLOW_MANAGE_STAKEHOLDERS_QUERY, CREATE_FLOW_MANAGE_STAKEHOLDERS_VALUE, } from "../../utils/flowSteps"; import { PublishedStakeholdersManagePanel } from "./PublishedStakeholdersManagePanel"; const VISIBLE_WHEN_COLLAPSED = 6; function InvitePersonRow({ email, youLabel, removeAria, onRemove, }: { email: string; youLabel?: string; removeAria: string; onRemove?: () => void; }) { return (

{email}

{youLabel ? (

{youLabel}

) : null}
{onRemove ? ( ) : null}
); } export function ConfirmStakeholdersScreen() { const searchParams = useSearchParams(); const { state, updateState, markCreateFlowInteraction } = useCreateFlow(); const t = useTranslation("create.reviewAndComplete.confirmStakeholders"); const manageStakeholdersIntent = searchParams?.get(CREATE_FLOW_MANAGE_STAKEHOLDERS_QUERY) === CREATE_FLOW_MANAGE_STAKEHOLDERS_VALUE; const editingPublishedRuleId = state.editingPublishedRuleId?.trim() ?? ""; const [ownerEmail, setOwnerEmail] = useState(null); const [draftEmail, setDraftEmail] = useState(""); const [fieldError, setFieldError] = useState(null); const [pasteResult, setPasteResult] = useState(null); const [expanded, setExpanded] = useState(false); useEffect(() => { let cancelled = false; void fetchAuthSession().then(({ user }) => { if (cancelled) return; const email = user?.email?.trim().toLowerCase() ?? ""; setOwnerEmail(email.length > 0 ? email : null); }); return () => { cancelled = true; }; }, []); const invitees = useMemo(() => { const raw = state.stakeholderEmails ?? []; const owner = ownerEmail; return raw .map((email) => email.trim().toLowerCase()) .filter((email) => email.length > 0 && email !== owner); }, [state.stakeholderEmails, ownerEmail]); const rows = useMemo(() => { const list: Array<{ email: string; isYou: boolean }> = []; if (ownerEmail) { list.push({ email: ownerEmail, isYou: true }); } for (const email of invitees) { list.push({ email, isYou: false }); } return list; }, [ownerEmail, invitees]); const visibleRows = expanded ? rows : rows.slice(0, VISIBLE_WHEN_COLLAPSED); const hiddenCount = Math.max(0, rows.length - visibleRows.length); const preview = membershipInvitePreview(state); const countLine = rows.length === 1 ? t("countOne") : t("countMany").replace("{count}", String(rows.length)); const persistInvitees = useCallback( (next: string[]) => { updateState({ stakeholderEmails: next }); }, [updateState], ); const addOne = useCallback( (raw: string): boolean => { const trimmed = raw.trim().toLowerCase(); if (!INVITE_EMAIL_PATTERN.test(trimmed)) { setFieldError(t("invalidEmail")); return false; } if (ownerEmail && trimmed === ownerEmail) { setFieldError(t("duplicateEmail")); return false; } if (invitees.includes(trimmed)) { setFieldError(t("duplicateEmail")); return false; } if (invitees.length >= MAX_STAKEHOLDER_EMAILS) { setFieldError(t("maxStakeholders")); return false; } persistInvitees([...invitees, trimmed]); return true; }, [invitees, ownerEmail, persistInvitees, t], ); const handleAdd = () => { markCreateFlowInteraction(); setPasteResult(null); if (addOne(draftEmail)) { setDraftEmail(""); setFieldError(null); } }; const handlePaste = (event: ClipboardEvent) => { const text = event.clipboardData.getData("text"); const tokens = text .split(/[\s,;]+/) .map((token) => token.trim()) .filter((token) => token.length > 0); if (tokens.length <= 1) return; event.preventDefault(); markCreateFlowInteraction(); setFieldError(null); const existing = ownerEmail ? [ownerEmail, ...invitees] : [...invitees]; const parsed = parseInvitePaste(text, existing, MAX_STAKEHOLDER_EMAILS); if (parsed.added.length > 0) { persistInvitees([...invitees, ...parsed.added]); } setDraftEmail(parsed.invalidRemainder); if (parsed.invalidRemainder.length > 0) { setFieldError(t("invalidEmail")); } const parts: string[] = []; if (parsed.added.length > 0) { const people = parsed.added.length === 1 ? t("peopleNounOne") : t("peopleNounMany"); parts.push( t("pasteAdded") .replace("{count}", formatInviteCountWord(parsed.added.length)) .replace("{people}", people), ); } const invalidCount = parsed.invalidRemainder .split(",") .map((item) => item.trim()) .filter((item) => item.length > 0).length; if (invalidCount === 1) { parts.push(t("pasteInvalidOne")); } else if (invalidCount > 1) { parts.push( t("pasteInvalidMany").replace( "{count}", formatInviteCountWord(invalidCount), ), ); } setPasteResult(parts.length > 0 ? parts.join(" ") : null); }; const handleRemove = (email: string) => { markCreateFlowInteraction(); setPasteResult(null); persistInvitees(invitees.filter((item) => item !== email)); }; const header = (
{preview ? ( ) : null}
); if (manageStakeholdersIntent) { return ( {editingPublishedRuleId.length > 0 ? ( ) : null} ); } return (
{ setDraftEmail(event.target.value); setFieldError(null); setPasteResult(null); }} onKeyDown={(event) => { if (event.key !== "Enter") return; event.preventDefault(); handleAdd(); }} />
{pasteResult ? (

{pasteResult}

) : null} {rows.length > 0 ? (
{visibleRows.map((row) => ( handleRemove(row.email) } /> ))} {hiddenCount > 0 ? (
{ markCreateFlowInteraction(); setExpanded(true); }} > {t("showMore").replace("{count}", String(hiddenCount))}
) : null}
) : null} {rows.length > 0 ? (

{countLine}

) : null}

{t("privacy")}

); }