import { useEffect, useMemo, useState } from 'react' import type { OrgCreateMemberInput, OrgSavedCreatePayload } from '../types/visual' interface OrgCreateResult extends OrgSavedCreatePayload { nonce: number } interface OrgCreateModalProps { open: boolean pending?: boolean result?: OrgCreateResult | null onClose: () => void onCreate: (organizationName: string, members: OrgCreateMemberInput[]) => void } type MemberDraft = { name: string responsibility: string prompt: string reportsToIndex: number | null } const INITIAL_MEMBERS: MemberDraft[] = [ { name: '', responsibility: '', prompt: '', reportsToIndex: null }, { name: '', responsibility: '', prompt: '', reportsToIndex: 0 }, ] function slugLabel(value: string): string { return value.trim() || 'Member' } export function OrgCreateModal({ open, pending, result, onClose, onCreate }: OrgCreateModalProps) { const [step, setStep] = useState(1) const [organizationName, setOrganizationName] = useState('') const [members, setMembers] = useState(INITIAL_MEMBERS) const [localError, setLocalError] = useState('') useEffect(() => { if (!open) return setStep(1) setOrganizationName('') setMembers(INITIAL_MEMBERS) setLocalError('') }, [open]) useEffect(() => { if (!open || !result) return if (result.ok) { onClose() return } setLocalError(result.error || 'Failed to create organization') }, [open, result, onClose]) const organizationValid = organizationName.trim().length > 0 const validMembers = useMemo( () => members.map((member, index) => ({ member, index })).filter(item => item.member.name.trim()), [members], ) const originalIndexToCreateIndex = useMemo( () => new Map(validMembers.map((item, createIndex) => [item.index, createIndex])), [validMembers], ) const membersValid = validMembers.length >= 2 const canCreate = organizationValid && membersValid && !pending const previewMembers = useMemo( () => validMembers.map(({ member, index }, createIndex) => { const mappedParent = member.reportsToIndex == null ? null : originalIndexToCreateIndex.get(member.reportsToIndex) return { ...member, roleName: slugLabel(member.name), managerName: mappedParent != null && mappedParent < createIndex ? slugLabel(validMembers[mappedParent]?.member.name || '') : 'Owner', index, } }), [originalIndexToCreateIndex, validMembers], ) if (!open) return null const updateMember = (index: number, patch: Partial) => { setMembers(prev => prev.map((member, idx) => idx === index ? { ...member, ...patch } : member)) } const addMember = () => { setMembers(prev => [...prev, { name: '', responsibility: '', prompt: '', reportsToIndex: 0 }]) } const removeMember = (index: number) => { setMembers(prev => { const next = prev.filter((_, idx) => idx !== index) return next.map((member, idx) => ({ ...member, reportsToIndex: member.reportsToIndex == null ? null : member.reportsToIndex >= index ? Math.max(0, member.reportsToIndex - 1) : member.reportsToIndex, })).map((member, idx) => idx === 0 ? { ...member, reportsToIndex: null } : member) }) } const submit = () => { if (!canCreate) return setLocalError('') onCreate( organizationName.trim(), validMembers.map(({ member }, createIndex) => { const mappedParent = member.reportsToIndex == null ? null : originalIndexToCreateIndex.get(member.reportsToIndex) return { name: member.name.trim(), responsibility: member.responsibility.trim(), prompt: member.prompt.trim(), reports_to_index: mappedParent != null && mappedParent < createIndex ? mappedParent : createIndex === 0 ? null : 0, } }), ) } return (
e.stopPropagation()}>
New organization

Create a saved org

{[ ['1', 'Name'], ['2', 'Members'], ['3', 'Review'], ].map(([id, label]) => ( Number(id) ? ' org-create-step--done' : ''}`}> {id}{label} ))}
{step === 1 && (
)} {step === 2 && (
{members.map((member, index) => (
updateMember(index, { name: e.target.value })} placeholder={index === 0 ? 'Lead role' : 'Member role'} /> updateMember(index, { responsibility: e.target.value })} placeholder="Responsibility" />