From 34454abc18e86083801d94eb44097054c537d5ca Mon Sep 17 00:00:00 2001 From: IliesAllali Date: Tue, 20 Oct 2026 14:27:49 +0200 Subject: [PATCH] =?UTF-8?q?M=C3=A9nage=20:=20retirer=20onze=20modules=20de?= =?UTF-8?q?=20l'interface=20que=20plus=20rien=20n'importe?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AddPersonButton, DropTrashZone, CreateMyTreeButton, EditModeOverlay.jsx (App importe EditSessionBar sous ce nom, sa feuille de style reste), InitialTreeWizard, InlinePersonForm, ProximityFeedback, useAddPersonDrag, graphAdapter et graphDataHelpers, avec leurs feuilles de style. Aucun import, test compris ; lint, 253 tests et build verts. Co-Authored-By: Claude Opus 5.5 --- frontend/src/components/AddPersonButton.css | 172 ------------ frontend/src/components/AddPersonButton.jsx | 84 ------ .../src/components/CreateMyTreeButton.css | 261 ----------------- .../src/components/CreateMyTreeButton.jsx | 93 ------- frontend/src/components/EditModeOverlay.jsx | 50 ---- frontend/src/components/InitialTreeWizard.jsx | 156 ----------- frontend/src/components/InlinePersonForm.css | 262 ------------------ frontend/src/components/InlinePersonForm.jsx | 215 -------------- frontend/src/components/ProximityFeedback.css | 146 ---------- frontend/src/components/ProximityFeedback.jsx | 72 ----- .../components/annotations/DropTrashZone.css | 91 ------ .../components/annotations/DropTrashZone.jsx | 22 -- frontend/src/hooks/useAddPersonDrag.js | 183 ------------ frontend/src/i18n/en/fiche.js | 2 +- frontend/src/utils/graphAdapter.js | 108 -------- frontend/src/utils/graphDataHelpers.js | 117 -------- 16 files changed, 1 insertion(+), 2033 deletions(-) delete mode 100644 frontend/src/components/AddPersonButton.css delete mode 100644 frontend/src/components/AddPersonButton.jsx delete mode 100644 frontend/src/components/CreateMyTreeButton.css delete mode 100644 frontend/src/components/CreateMyTreeButton.jsx delete mode 100644 frontend/src/components/EditModeOverlay.jsx delete mode 100644 frontend/src/components/InitialTreeWizard.jsx delete mode 100644 frontend/src/components/InlinePersonForm.css delete mode 100644 frontend/src/components/InlinePersonForm.jsx delete mode 100644 frontend/src/components/ProximityFeedback.css delete mode 100644 frontend/src/components/ProximityFeedback.jsx delete mode 100644 frontend/src/components/annotations/DropTrashZone.css delete mode 100644 frontend/src/components/annotations/DropTrashZone.jsx delete mode 100644 frontend/src/hooks/useAddPersonDrag.js delete mode 100644 frontend/src/utils/graphAdapter.js delete mode 100644 frontend/src/utils/graphDataHelpers.js diff --git a/frontend/src/components/AddPersonButton.css b/frontend/src/components/AddPersonButton.css deleted file mode 100644 index ad59fb0..0000000 --- a/frontend/src/components/AddPersonButton.css +++ /dev/null @@ -1,172 +0,0 @@ -/* ============================================================ - Add Person Button - FAB draggable pour ajouter personne - ============================================================ */ - -.add-person-button { - position: fixed; - width: 64px; - height: 64px; - border-radius: 50%; - - background: rgba(242, 239, 234, 0.95); - backdrop-filter: blur(20px); - -webkit-backdrop-filter: blur(20px); - border: 2px solid rgba(147, 64, 42, 0.3); - - box-shadow: - 0 8px 32px rgba(42, 38, 34, 0.25), - 0 2px 8px rgba(42, 38, 34, 0.12); - - cursor: grab; - user-select: none; - - display: flex; - align-items: center; - justify-content: center; - - transition: all 0.25s cubic-bezier(0.22, 1, 0.36, 1); - z-index: 900; - - /* Position initiale : bas-gauche */ - bottom: 24px; - left: 24px; -} - -.add-person-button:hover { - transform: scale(1.12); - box-shadow: - 0 12px 40px rgba(147, 64, 42, 0.35), - 0 4px 12px rgba(42, 38, 34, 0.18); - border-color: rgba(147, 64, 42, 0.5); -} - -.add-person-button.dragging { - cursor: grabbing; - transform: scale(1.2) rotate(5deg); - box-shadow: - 0 16px 48px rgba(147, 64, 42, 0.45), - 0 6px 16px rgba(42, 38, 34, 0.25); - border-color: rgba(147, 64, 42, 0.7); - z-index: 1100; -} - -/* Icône + */ -.add-person-button-icon { - font-size: 32px; - font-weight: 300; - color: #93402A; - line-height: 1; - filter: drop-shadow(0 1px 3px rgba(42, 38, 34, 0.15)); - transition: transform 0.2s ease; -} - -.add-person-button:hover .add-person-button-icon { - transform: rotate(90deg); -} - -.add-person-button.dragging .add-person-button-icon { - transform: rotate(180deg); -} - -/* Pulse animation sur apparition */ -@keyframes addPersonPulse { - 0% { - opacity: 0; - transform: scale(0.7); - } - 50% { - transform: scale(1.15); - } - 100% { - opacity: 1; - transform: scale(1); - } -} - -.add-person-button.entering { - animation: addPersonPulse 0.5s cubic-bezier(0.22, 1, 0.36, 1); -} - -/* Tooltip */ -.add-person-button-tooltip { - position: absolute; - top: 50%; - left: calc(100% + 12px); - transform: translateY(-50%); - - padding: 8px 14px; - background: rgba(42, 38, 34, 0.95); - color: #FFFFFF; - font-size: 14px; - font-weight: 500; - border-radius: 8px; - white-space: nowrap; - - opacity: 0; - pointer-events: none; - transition: opacity 0.2s ease; - - box-shadow: 0 4px 12px rgba(42, 38, 34, 0.2); -} - -.add-person-button:hover .add-person-button-tooltip { - opacity: 1; -} - -.add-person-button.dragging .add-person-button-tooltip { - opacity: 0; -} - -/* Instructions drag (affichées pendant drag) */ -.add-person-drag-hint { - position: fixed; - top: 24px; - left: 50%; - transform: translateX(-50%); - z-index: 1050; - - padding: 10px 18px; - background: rgba(242, 239, 234, 0.98); - backdrop-filter: blur(24px); - border-radius: 50px; - border: 1px solid rgba(147, 64, 42, 0.2); - - font-size: 14px; - font-weight: 450; - color: #2A2622; - - box-shadow: - 0 6px 24px rgba(42, 38, 34, 0.18), - 0 2px 8px rgba(42, 38, 34, 0.08); - - animation: hintSlideDown 0.3s cubic-bezier(0.22, 1, 0.36, 1); -} - -@keyframes hintSlideDown { - from { - opacity: 0; - transform: translateX(-50%) translateY(-10px); - } - to { - opacity: 1; - transform: translateX(-50%) translateY(0); - } -} - -/* Responsive mobile */ -@media (max-width: 768px) { - .add-person-button { - width: 56px; - height: 56px; - bottom: 20px; - left: 20px; - } - - .add-person-button-icon { - font-size: 28px; - } - - .add-person-button-tooltip { - display: none; - } -} diff --git a/frontend/src/components/AddPersonButton.jsx b/frontend/src/components/AddPersonButton.jsx deleted file mode 100644 index 7087c49..0000000 --- a/frontend/src/components/AddPersonButton.jsx +++ /dev/null @@ -1,84 +0,0 @@ -import { useRef, useEffect, useState } from 'react' -import { Plus, Baby, Heart, Users, MapPin } from 'lucide-react' -import './AddPersonButton.css' -import { t } from '../i18n/index.js' - -/** - * Bouton FAB draggable pour ajouter une personne - * Utilisé en mode édition pour drag-and-drop dans la galaxie - * - * @param {Object} props - * @param {boolean} props.isDragging - État dragging (du hook) - * @param {Object} props.dragPosition - Position drag {x, y} - * @param {Function} props.onDragStart - Handler début drag - * @param {string} props.linkType - Type de lien détecté ('child', 'spouse', 'sibling') - */ -function AddPersonButton({ isDragging, dragPosition, onDragStart, linkType }) { - const buttonRef = useRef(null) - const [entering, setEntering] = useState(true) - - // Animation d'entrée - useEffect(() => { - const timer = setTimeout(() => setEntering(false), 500) - return () => clearTimeout(timer) - }, []) - - const handleMouseDown = (e) => { - e.preventDefault() - if (!buttonRef.current) return - - const rect = buttonRef.current.getBoundingClientRect() - const buttonPosition = { - x: rect.left + rect.width / 2, - y: rect.top + rect.height / 2, - } - - onDragStart?.(e, buttonPosition) - } - - // Position inline si dragging - const style = isDragging - ? { - left: `${dragPosition.x - 32}px`, - top: `${dragPosition.y - 32}px`, - transition: 'none', - } - : {} - - // Message tooltip selon linkType - let tooltipText = t('Glisser pour ajouter une personne') - if (isDragging) { - if (linkType === 'child') tooltipText = t('Ajouter un enfant') - else if (linkType === 'spouse') tooltipText = t('Ajouter un conjoint') - else if (linkType === 'sibling') tooltipText = t('Ajouter un frère/sœur') - else tooltipText = t('Relâcher pour créer') - } - - return ( - <> - - - {/* Hint pendant drag */} - {isDragging && ( -
- {linkType === 'child' && <> {t('Enfant')}} - {linkType === 'spouse' && <> {t('Conjoint')}} - {linkType === 'sibling' && <> {t('Frère·Sœur')}} - {!linkType && <> {t('Positionnez la personne')}} -
- )} - - ) -} - -export default AddPersonButton diff --git a/frontend/src/components/CreateMyTreeButton.css b/frontend/src/components/CreateMyTreeButton.css deleted file mode 100644 index fa43248..0000000 --- a/frontend/src/components/CreateMyTreeButton.css +++ /dev/null @@ -1,261 +0,0 @@ -/* ============================================================ - CreateMyTreeButton — Pill + Panel morph, bas-droite - Même langage visuel que la ContextualNavbar - ============================================================ */ - -/* ---------------------------------------------------------- - Pill bouton (état fermé) — même aesthetic que la navbar - ---------------------------------------------------------- */ -.cmt-pill { - position: fixed; - bottom: 100px; - right: 20px; - z-index: 1001; - - display: flex; - align-items: center; - padding: 13px 22px; - border: 1px solid rgba(42, 38, 34, 0.1); - border-radius: 50px; - background: rgba(242, 239, 234, 0.97); - backdrop-filter: blur(20px); - -webkit-backdrop-filter: blur(20px); - box-shadow: - 0 4px 40px rgba(42, 38, 34, 0.22), - 0 1px 6px rgba(42, 38, 34, 0.1); - - font-family: 'DM Sans', system-ui, sans-serif; - font-size: 14px; - font-weight: 500; - color: #2A2622; - cursor: pointer; - white-space: nowrap; - - animation: cmtPillIn 0.5s cubic-bezier(0.22, 1, 0.36, 1); - transition: box-shadow 0.2s ease, transform 0.2s ease, background 0.2s ease; -} - -@keyframes cmtPillIn { - from { - opacity: 0; - transform: translateY(12px) scale(0.9); - } - to { - opacity: 1; - transform: translateY(0) scale(1); - } -} - -.cmt-pill:hover { - background: rgba(242, 239, 234, 1); - box-shadow: - 0 8px 48px rgba(42, 38, 34, 0.28), - 0 2px 8px rgba(42, 38, 34, 0.12); - transform: translateY(-2px); -} - -.cmt-pill:active { - transform: translateY(0) scale(0.97); -} - -/* ---------------------------------------------------------- - Panel (état ouvert) — card qui morphe depuis la pill - ---------------------------------------------------------- */ -.cmt-panel { - position: fixed; - bottom: 100px; - right: 20px; - z-index: 1001; - - width: 300px; - background: rgba(255, 255, 255, 0.97); - backdrop-filter: blur(28px); - -webkit-backdrop-filter: blur(28px); - border-radius: 20px; - border: 1px solid rgba(147, 64, 42, 0.18); - box-shadow: - 0 12px 48px rgba(42, 38, 34, 0.22), - 0 2px 10px rgba(42, 38, 34, 0.1); - - transform-origin: bottom right; - animation: cmtMorphIn 0.38s cubic-bezier(0.22, 1, 0.36, 1); - overflow: hidden; -} - -@keyframes cmtMorphIn { - 0% { - opacity: 0; - transform: scale(0.55) translateY(8px); - border-radius: 50px; - } - 60% { - border-radius: 22px; - } - 100% { - opacity: 1; - transform: scale(1) translateY(0); - border-radius: 20px; - } -} - -/* Header */ -.cmt-panel-header { - display: flex; - align-items: center; - justify-content: space-between; - padding: 16px 18px 12px; - border-bottom: 1px solid rgba(147, 64, 42, 0.1); -} - -.cmt-panel-title { - font-family: 'DM Sans', system-ui, sans-serif; - font-size: 13px; - font-weight: 600; - color: #2A2622; - line-height: 1.3; -} - -.cmt-panel-close { - display: flex; - align-items: center; - justify-content: center; - width: 28px; - height: 28px; - border: none; - border-radius: 50%; - background: rgba(42, 38, 34, 0.07); - color: #6B645D; - font-size: 12px; - cursor: pointer; - flex-shrink: 0; - transition: background 0.15s, color 0.15s; -} - -.cmt-panel-close:hover { - background: rgba(42, 38, 34, 0.13); - color: #2A2622; -} - -/* Body */ -.cmt-panel-body { - padding: 16px 18px 18px; -} - -.cmt-label { - display: block; - font-family: 'DM Sans', system-ui, sans-serif; - font-size: 12px; - color: #6B645D; - margin-bottom: 7px; - letter-spacing: 0.02em; -} - -.cmt-input { - width: 100%; - box-sizing: border-box; - border-radius: 12px; - border: 1px solid rgba(147, 64, 42, 0.25); - background: rgba(255, 255, 255, 0.9); - color: #2a2622; - padding: 10px 13px; - font-family: 'DM Sans', system-ui, sans-serif; - font-size: 14px; - outline: none; - transition: border-color 0.15s, box-shadow 0.15s; -} - -.cmt-input:focus { - border-color: #d2694a; - box-shadow: 0 0 0 3px rgba(210, 105, 74, 0.12); -} - -.cmt-slug-preview { - margin-top: 8px; - font-family: 'DM Sans', system-ui, sans-serif; - font-size: 11px; - color: #93402A; -} - -.cmt-slug-preview code { - background: rgba(147, 64, 42, 0.1); - border-radius: 4px; - padding: 1px 5px; - color: #2a2622; -} - -.cmt-error { - margin-top: 8px; - background: rgba(171, 49, 49, 0.08); - border: 1px solid rgba(171, 49, 49, 0.25); - color: #862f2f; - border-radius: 10px; - padding: 9px 12px; - font-family: 'DM Sans', system-ui, sans-serif; - font-size: 12px; -} - -.cmt-btn-create { - margin-top: 14px; - width: 100%; - padding: 11px 16px; - border-radius: 12px; - border: none; - background: #2a2622; - color: #ffffff; - font-family: 'DM Sans', system-ui, sans-serif; - font-size: 14px; - font-weight: 500; - cursor: pointer; - transition: background 0.15s, box-shadow 0.15s, transform 0.15s; -} - -.cmt-btn-create:hover:not(:disabled) { - background: linear-gradient(135deg, #93402a, #2a2622); - box-shadow: 0 4px 14px rgba(147, 64, 42, 0.32); - transform: translateY(-1px); -} - -.cmt-btn-create:active:not(:disabled) { - transform: translateY(0); -} - -.cmt-btn-create:disabled { - opacity: 0.5; - cursor: not-allowed; -} - -/* ---------------------------------------------------------- - Mobile — bottom sheet - ---------------------------------------------------------- */ -@media (max-width: 768px) { - .cmt-pill { - bottom: 84px; - right: 12px; - font-size: 13px; - padding: 11px 18px; - } - - .cmt-panel { - bottom: 0; - right: 0; - left: 0; - width: 100%; - border-radius: 20px 20px 0 0; - transform-origin: bottom center; - } - - @keyframes cmtMorphIn { - from { - opacity: 0; - transform: translateY(100%); - } - to { - opacity: 1; - transform: translateY(0); - } - } - - .cmt-panel-body { - padding: 16px 20px 32px; - } -} diff --git a/frontend/src/components/CreateMyTreeButton.jsx b/frontend/src/components/CreateMyTreeButton.jsx deleted file mode 100644 index ebbf6cd..0000000 --- a/frontend/src/components/CreateMyTreeButton.jsx +++ /dev/null @@ -1,93 +0,0 @@ -import { useState, useMemo } from 'react' -import './CreateMyTreeButton.css' -import PzBusy from './PzBusy.jsx' -import { t } from '../i18n/index.js' - -function normalizeSlug(input) { - return input - .toLowerCase() - .normalize('NFD') - .replace(/[\u0300-\u036f]/g, '') - .replace(/[^a-z0-9]+/g, '-') - .replace(/^-+|-+$/g, '') -} - -const CreateMyTreeButton = ({ onCreateTree, loading }) => { - const [open, setOpen] = useState(false) - const [name, setName] = useState('') - const [error, setError] = useState('') - - const slug = useMemo(() => normalizeSlug(name.trim()), [name]) - const canSubmit = name.trim().length >= 2 && slug.length >= 3 && !loading - - const handleOpen = () => { - setError('') - setOpen(true) - } - - const handleClose = () => { - setOpen(false) - setName('') - setError('') - } - - const handleSubmit = async (e) => { - e.preventDefault() - if (!canSubmit) return - setError('') - const result = await onCreateTree(name.trim()) - if (result !== null && result !== undefined) { - setError(typeof result === 'string' ? result : t('Erreur lors de la création. Essayez un autre nom.')) - } - } - - return ( - <> - {!open && ( - - )} - - {open && ( -
-
- {t('Créer votre arbre dès maintenant')} - -
-
-
- - setName(e.target.value)} - placeholder={t('Ex : Famille Martin')} - required - /> - {slug && ( -
- {t('Adresse :')} {slug} -
- )} - {error &&
{error}
} - -
-
-
- )} - - ) -} - -export default CreateMyTreeButton diff --git a/frontend/src/components/EditModeOverlay.jsx b/frontend/src/components/EditModeOverlay.jsx deleted file mode 100644 index a58ce18..0000000 --- a/frontend/src/components/EditModeOverlay.jsx +++ /dev/null @@ -1,50 +0,0 @@ -import { useMemo } from 'react' -import { X } from 'lucide-react' -import './EditModeOverlay.css' -import { t } from '../i18n/index.js' - -function EditModeOverlay({ active, hasDraft, role = 'contributor', onDeactivate, onSubmit }) { - const isAdmin = role === 'admin' - - const borderClass = useMemo(() => ( - `edit-mode-border ${isAdmin ? 'edit-mode-border--admin' : 'edit-mode-border--contributor'}` - ), [isAdmin]) - - const toastLabel = isAdmin ? t('Mode édition actif') : t('Mode contribution actif') - const submitLabel = isAdmin ? t("Mettre à jour l'arbre") : t('Envoyer les modifications') - - if (!active) return null - - return ( - <> -