mirror of
https://github.com/IliesAllali/parenthese.git
synced 2026-11-15 08:32:03 +00:00
Module frontend/src/i18n : le texte français sert de clé, dictionnaires anglais par zone, langue du navigateur puis choix mémorisé, ?lang=en pour forcer. Bascule dans le menu du compte, la pastille visiteur, le menu Plus, l'écran d'accès et la connexion. Démo traduite (noms et lieux gardés). Liens d'aide et de vie privée selon la langue. Textes français sans accents corrigés. Test : chaque texte passé à t() a sa traduction. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
167 lines
6.3 KiB
React
167 lines
6.3 KiB
React
import { Fragment, useEffect, useState } from 'react'
|
|
import logoUrl from '../assets/parenthese-logo.svg?url'
|
|
import './AccountScreens.css'
|
|
import PzBusy from './PzBusy.jsx'
|
|
import LanguageSwitch from './navbar/LanguageSwitch'
|
|
import { t } from '../i18n/index.js'
|
|
import { readContributorName, saveContributorName } from '../utils/contributorName.js'
|
|
|
|
// Phrase traduite d'un bloc : les passages entre astérisques passent en <em>, {slot} reçoit un nœud.
|
|
function richText(text, slotNode = null) {
|
|
return text.split(/(\{slot\})/).flatMap((chunk, chunkIndex) => (
|
|
chunk === '{slot}'
|
|
? [<Fragment key={`s${chunkIndex}`}>{slotNode}</Fragment>]
|
|
: chunk.split('*').map((part, index) => (index % 2 ? <em key={`${chunkIndex}-${index}`}>{part}</em> : part))
|
|
))
|
|
}
|
|
|
|
const AccessGate = ({
|
|
initialTreeId,
|
|
treeName,
|
|
treeDescription = '',
|
|
treeOwnerName = '',
|
|
treeNotFound = false,
|
|
loading,
|
|
errorMessage,
|
|
onSubmit,
|
|
onUseAccount,
|
|
}) => {
|
|
const [treeId, setTreeId] = useState(initialTreeId || '')
|
|
const [password, setPassword] = useState('')
|
|
// Facultatif : la personne qui partage l'arbre voit qui est passé (onglet Visites)
|
|
const [firstName, setFirstName] = useState(() => readContributorName())
|
|
|
|
useEffect(() => {
|
|
setTreeId(initialTreeId || '')
|
|
}, [initialTreeId])
|
|
|
|
const handleSubmit = async (event) => {
|
|
event.preventDefault()
|
|
if (!treeId || !password || loading) {
|
|
return
|
|
}
|
|
|
|
saveContributorName(firstName)
|
|
await onSubmit(treeId.trim(), password)
|
|
}
|
|
|
|
// Jamais d'adresse email avant le mot de passe : seul un nom explicite peut s'afficher
|
|
const ownerLabel = treeOwnerName || ''
|
|
|
|
return (
|
|
<div className="access-gate pz-screen">
|
|
<div className="pz-screen-inner">
|
|
<img src={logoUrl} alt="Parenthèse" className="pz-screen-logo" />
|
|
|
|
<form className="pz-card pz-screen-card" onSubmit={handleSubmit}>
|
|
<div className="pz-auth-head">
|
|
<p className="pz-eyebrow">{t('Arbre partagé')}</p>
|
|
{treeNotFound ? (
|
|
<h1 className="pz-title">{richText(t('Cet arbre est *introuvable*'))}</h1>
|
|
) : (
|
|
<h1 className="pz-title">
|
|
{treeName ? richText(t('Ouvrir {slot}'), <em>{treeName}</em>) : richText(t('Ouvrir un arbre *partagé*'))}
|
|
</h1>
|
|
)}
|
|
<p className="pz-sub">
|
|
{treeNotFound
|
|
? t("Le lien est peut-être incomplet, ou l'arbre a été supprimé. Demandez un nouveau lien à la personne qui vous l'a envoyé.")
|
|
: treeName
|
|
? t('Entrez le mot de passe reçu avec le lien. Pas besoin de compte pour regarder.')
|
|
: t('Entrez le code et le mot de passe reçus avec votre lien de partage.')}
|
|
</p>
|
|
</div>
|
|
|
|
{!treeNotFound && (ownerLabel || treeDescription) && (
|
|
<div className="pz-gate-owner">
|
|
{ownerLabel && (
|
|
<span className="pz-gate-mono" aria-hidden="true">{ownerLabel.trim().charAt(0).toUpperCase()}</span>
|
|
)}
|
|
<div className="pz-gate-owner-text">
|
|
{ownerLabel && <p className="access-gate-tree-meta pz-small">{richText(t('Partagé par {slot}'), <strong>{ownerLabel}</strong>)}</p>}
|
|
{treeDescription && <p className="pz-small">{treeDescription}</p>}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{!treeNotFound && (
|
|
<div className="pz-auth-form">
|
|
{!initialTreeId && (
|
|
<div className="pz-field">
|
|
<label htmlFor="treeId">{t("Code de l'arbre")}</label>
|
|
<input
|
|
id="treeId"
|
|
type="text"
|
|
value={treeId}
|
|
onChange={(event) => setTreeId(event.target.value)}
|
|
disabled={loading}
|
|
placeholder={t('Il commence souvent par cm')}
|
|
autoComplete="off"
|
|
required
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
<div className="pz-field">
|
|
<label htmlFor="visitorFirstName">{t('Votre prénom')}</label>
|
|
<input
|
|
id="visitorFirstName"
|
|
type="text"
|
|
value={firstName}
|
|
onChange={(event) => setFirstName(event.target.value)}
|
|
disabled={loading}
|
|
autoComplete="given-name"
|
|
maxLength={60}
|
|
/>
|
|
<p className="pz-hint">{t("Facultatif. Il sert à savoir qui est venu voir l'arbre.")}</p>
|
|
</div>
|
|
|
|
<div className="pz-field">
|
|
<label htmlFor="sharePassword">{t('Mot de passe de partage')}</label>
|
|
<input
|
|
id="sharePassword"
|
|
type="password"
|
|
value={password}
|
|
onChange={(event) => setPassword(event.target.value)}
|
|
disabled={loading}
|
|
autoFocus={Boolean(initialTreeId)}
|
|
required
|
|
/>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{errorMessage && <div className="access-gate-error pz-error" role="alert">{errorMessage}</div>}
|
|
|
|
{!treeNotFound && (
|
|
<button type="submit" className="pz-btn pz-btn--primary pz-btn--block" disabled={loading}>
|
|
<PzBusy busy={loading} busyLabel={t("Ouverture de l'arbre")}>{t("Ouvrir l'arbre")}</PzBusy>
|
|
</button>
|
|
)}
|
|
|
|
{treeNotFound && onUseAccount && (
|
|
<button type="button" className="pz-btn pz-btn--primary pz-btn--block" onClick={() => onUseAccount('register')} disabled={loading}>
|
|
{t('Créer mon arbre')}
|
|
</button>
|
|
)}
|
|
</form>
|
|
|
|
{onUseAccount && !treeNotFound && (
|
|
<div className="pz-account-foot">
|
|
<button type="button" className="pz-btn pz-btn--ghost pz-btn--sm" onClick={() => onUseAccount('login')} disabled={loading}>
|
|
{t('Se connecter avec un compte')}
|
|
</button>
|
|
<button type="button" className="pz-btn pz-btn--ghost pz-btn--sm" onClick={() => onUseAccount('register')} disabled={loading}>
|
|
{t('Créer un compte')}
|
|
</button>
|
|
</div>
|
|
)}
|
|
|
|
<LanguageSwitch className="pz-lang--screen" />
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default AccessGate
|