import { useEffect, useState } from 'react'
import { inviteManager, invitationUrl, listManagers, removeManager, renewInvitationLink, revokeInvitation } from '../api/managersApi.js'
import PzBusy from './PzBusy.jsx'
import { dateLocale, t } from '../i18n/index.js'
import { initialOf } from '../utils/initial.js'
import Skeleton from './Skeleton.jsx'
import { toOpaqueTreeId, trackAppEvent } from '../utils/analytics.js'
// Onglet Gestion des paramètres de l'arbre (issue #145). Le propriétaire invite des co-gestionnaires par leur
// adresse : Parenthèse n'envoie pas de mail, il donne un lien à transmettre. Les co-gestionnaires voient la liste.
const ERRORS = {
already_manager: t('Cette adresse gère déjà cet arbre.'),
invalid_email: t('Cette adresse email ne semble pas valide.'),
too_many_invitations: t('Trop d’invitations en attente. Annulez-en une avant d’en envoyer une autre.'),
}
function personInitial(person) {
return initialOf(person.firstName || person.email)
}
const TreeManagersSection = ({ treeId, authToken, currentUserId = null, oidcName = '', onOpenShare }) => {
const [state, setState] = useState({ loading: true, error: '', data: null })
const [email, setEmail] = useState('')
const [busy, setBusy] = useState('')
const [message, setMessage] = useState({ error: '', success: '' })
// Lien tout juste créé, montré en clair si le presse-papiers est refusé
const [link, setLink] = useState(null)
const [confirmRemove, setConfirmRemove] = useState(null)
// Co-gestionnaire tout juste retiré : il a pu lire le mot de passe de partage, on propose de le changer
const [removedName, setRemovedName] = useState('')
const load = () => listManagers(treeId, authToken)
.then((data) => setState({ loading: false, error: '', data }))
.catch(() => setState({ loading: false, error: t('Impossible de charger les gestionnaires.'), data: null }))
useEffect(() => {
setState({ loading: true, error: '', data: null })
load()
}, [treeId, authToken]) // eslint-disable-line react-hooks/exhaustive-deps
const offerLink = async (token, forEmail) => {
const url = invitationUrl(token)
let copied
try { await navigator.clipboard.writeText(url); copied = true } catch { copied = false }
// Le lien créé compte comme un partage même si le presse-papiers est refusé (il est alors montré en clair)
trackAppEvent('share_copied', { what: 'manager_invite', copied, tree_id: toOpaqueTreeId(treeId) })
setLink({ url, email: forEmail, copied })
}
const handleInvite = async (event) => {
event.preventDefault()
if (!email.trim() || busy) return
setBusy('invite')
setMessage({ error: '', success: '' })
try {
const { token, invitation } = await inviteManager(treeId, email.trim(), authToken)
setEmail('')
await offerLink(token, invitation.email)
await load()
} catch (error) {
setMessage({ error: ERRORS[error?.payload?.error] || t('Impossible d’envoyer l’invitation.'), success: '' })
} finally {
setBusy('')
}
}
const handleCopy = async (invitation) => {
setBusy(`link-${invitation.id}`)
setMessage({ error: '', success: '' })
try {
const { token } = await renewInvitationLink(treeId, invitation.id, authToken)
await offerLink(token, invitation.email)
} catch {
setMessage({ error: t('Impossible de créer le lien.'), success: '' })
} finally {
setBusy('')
}
}
const handleRevoke = async (invitation) => {
setBusy(`revoke-${invitation.id}`)
try {
await revokeInvitation(treeId, invitation.id, authToken)
if (link?.email === invitation.email) setLink(null)
await load()
} catch {
setMessage({ error: t('Impossible d’annuler l’invitation.'), success: '' })
} finally {
setBusy('')
}
}
const handleRemove = async (manager) => {
setBusy(`remove-${manager.userId}`)
try {
await removeManager(treeId, manager.userId, authToken)
setConfirmRemove(null)
setMessage({ error: '', success: '' })
setRemovedName(manager.firstName || manager.email)
await load()
} catch {
setMessage({ error: t('Impossible de retirer ce co-gestionnaire.'), success: '' })
} finally {
setBusy('')
}
}
if (state.loading) return
{t('Les co-gestionnaires modifient l’arbre directement et valident les propositions de la famille, comme vous. Seul le propriétaire invite, retire un co-gestionnaire ou supprime l’arbre.')}
{managers.map((manager) => { const isYou = manager.userId === currentUserId return ({t('Envoyez-le par message ou par mail. Il sert une fois et vaut 14 jours. « Copier le lien » en crée un nouveau, l’ancien ne marche plus.')}
{t('Cette personne a pu voir le mot de passe de partage : avec lui, elle peut encore ouvrir l’arbre en lecture. Pour lui couper aussi la lecture, changez-le, puis donnez le nouveau à la famille.')}
{onOpenShare && ( )}