/** * ========================================================================= * PLATEFORME ECO-PANNEAU.FR - VERSION 1.0.0 * Interface Client - Onglets : Mes Panneaux et Livraisons * ========================================================================= */ // ========================================================================= // 1. ONGLET : MES PANNEAUX // ========================================================================= window.ClientPanelsTab = ({ data, myClientData, refreshData, showToast, setActiveTab, setManagingPanneau, setValidationErrors, setModalStep, setIsModalOpen, setPreviewPanneau, setPanelTeamModal, setArchiveConfig, handleDeletePanelClick, setConfirmDialog }) => { const { useState } = React; const [isSaving, setIsSaving] = useState(false); // RÉCUPÉRATION SÉCURISÉE DES ICÔNES ET COMPOSANTS const BuildingIcon = window.BuildingIcon || (() => null); const PlusIcon = window.PlusIcon || (() => null); const AlertTriangleIcon = window.AlertTriangleIcon || (() => null); const UsersIcon = window.UsersIcon || (() => null); const CheckCircleIcon = window.CheckCircleIcon || (() => null); const Trash2Icon = window.Trash2Icon || (() => null); const MessageSquareIcon = window.MessageSquareIcon || (() => null); const EyeIcon = window.EyeIcon || (() => null); const QrCodeIcon = window.QrCodeIcon || (() => null); const Share2Icon = window.Share2Icon || (() => null); const LockIcon = window.LockIcon || (() => null); const EditIcon = window.EditIcon || (() => null); const ArchiveIcon = window.ArchiveIcon || (() => null); const PowerIcon = window.PowerIcon || (() => null); const CopyIcon = window.CopyIcon || (() => null); const XIcon = window.XIcon || (() => null); const StatusBadge = window.StatusBadge || (({status}) => {status}); // Fallback robuste avec restauration de la soumission native au clavier const Button = window.Button || (({children, onClick, className, disabled, title, icon: Icon, type}) => { const handleAction = (e) => { if (disabled) return; if (onClick) onClick(e); if (type === 'submit') { const form = e.currentTarget.closest('form'); if (form && typeof form.requestSubmit === 'function') form.requestSubmit(); } }; return (
{ if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); handleAction(e); } }} className={`flex items-center justify-center gap-2 cursor-pointer transition ${className || ''} ${disabled ? 'opacity-50 cursor-not-allowed' : ''}`} title={title} > {Icon && } {children} {type === 'submit' && }
); }); // FACTORISATION : Logique métier d'accès aux panneaux const { pendingInvites, visiblePanels } = window.getPanelAccessRights ? window.getPanelAccessRights(data.panneaux || [], myClientData.id) : { pendingInvites:[], visiblePanels:[] }; const isSuspended = myClientData.paymentStatus === 'suspended'; const purchasesAllowed = data.settings?.allow_new_purchases !== '0'; const respondToInvite = async (panneauId, action) => { const d = await window.apiFetch('panneaux/collaborators/respond', { body: { panneau_id: panneauId, action }, setLoading: setIsSaving, successMessage: "Invitation " + (action === 'accept' ? 'acceptée' : 'refusée') + " avec succès." }); if (d) refreshData(); }; return (

Mes panneaux

Gestion de vos affichages et de vos collaborations.

{!isSuspended && ( )}
{(!purchasesAllowed) && (

La validation de nouvelles commandes est temporairement suspendue. Vous pouvez toujours préparer vos brouillons.

)} {pendingInvites.length > 0 && (

Invitations en attente

{/* 3 colonnes décalées à 2xl */}
{pendingInvites.map((p, i) => { const collab = p.collaborators?.find(c => c.uid === myClientData.id); return (

{p.name}

Invitation à collaborer envoyée par {collab?.inviter_name || 'le propriétaire'} ({collab?.inviter_company || 'Société'})

); })}
)} {/* 3 colonnes décalées à 2xl */}
{visiblePanels.map((c, i) => { const isOwner = c.client_uid === myClientData.id; const myCollabData = !isOwner ? c.collaborators?.find(col => col.uid === myClientData.id) : null; const canEdit = isOwner || myCollabData?.rights?.can_edit; // Calcul robuste du délai d'expiration des brouillons let draftCountdown = 100; if (c.status === 'Brouillon') { const timeStr = c.updated_at || c.created_at; if (timeStr) { const parsedTime = new Date(String(timeStr).replace(' ', 'T')).getTime(); if (!isNaN(parsedTime)) { const diffDays = Math.floor((Date.now() - parsedTime) / (1000 * 3600 * 24)); draftCountdown = Math.max(0, 100 - diffDays); } } } return (
{!isOwner && (
Collaboration
)}

{c.name || 'Brouillon sans nom'}

{c.location || 'Localisation non définie'}

{c.status === 'Actif' && (

Scans QR

{c.views}

Abonnement

{c.offerType === 'rental' ? 'Location' : 'Achat'}

)} {c.status === 'Brouillon' && (
Suppression automatique dans {draftCountdown} jour(s)
)} {c.status === 'Suppression programmée' && (
Suppression complète à la fin de la période
)}
{c.status === 'Actif' && ( <>
{ const u = new URL(window.location); u.searchParams.set('chat_id', 'GROUP_' + c.id); window.history.replaceState({}, '', u); setActiveTab('messages'); }} title="Discussion de l'équipe du projet" className="flex-1 sm:flex-none justify-center bg-blue-50 text-blue-700 hover:bg-blue-100 px-4 py-2 rounded-xl text-xs font-bold transition flex items-center gap-2 shadow-sm cursor-pointer"> Équipe
{isOwner && ( )} {canEdit && ( <>
{ setManagingPanneau(c); setModalStep('legal_vault'); setIsModalOpen(true); }} title="Documents d'huissier" className="flex-1 sm:flex-none justify-center px-4 py-2 rounded-xl text-xs font-bold bg-slate-900 text-white hover:bg-slate-800 flex items-center gap-2 transition shadow-sm cursor-pointer"> Coffre-fort
{!isSuspended && ( )} )} {isOwner && ( <> )} )} {c.status === 'Brouillon' && !isSuspended && ( <> {(isOwner || canEdit) && ( )} {isOwner && ( )} )} {isOwner && c.status !== 'Suppression programmée' && ( )}
); })} {visiblePanels.length === 0 && (

Aucun panneau

Créez votre premier panneau pour démarrer ou attendez une invitation à collaborer.

)}
); }; // ========================================================================= // 2. ONGLET : LIVRAISONS // ========================================================================= window.ClientLivraisonsTab = ({ data, myClientData, refreshData, showToast }) => { const { useState, useEffect } = React; const [deliveryConfirmModal, setDeliveryConfirmModal] = useState(null); const [isSaving, setIsSaving] = useState(false); // RÉCUPÉRATION SÉCURISÉE DES ICÔNES ET COMPOSANTS const PackageIcon = window.PackageIcon || (() => null); const MapPinIcon = window.MapPinIcon || (() => null); const CheckCircleIcon = window.CheckCircleIcon || (() => null); const ExternalLinkIcon = window.ExternalLinkIcon || (() => null); const LoaderIcon = window.LoaderIcon || (() => null); const Modal = window.Modal || (() => null); const StatusBadge = window.StatusBadge || (({status}) => {status}); // Fallback robuste avec restauration de la soumission native au clavier const Button = window.Button || (({children, onClick, className, disabled, title, icon: Icon, type}) => { const handleAction = (e) => { if (disabled) return; if (onClick) onClick(e); if (type === 'submit') { const form = e.currentTarget.closest('form'); if (form && typeof form.requestSubmit === 'function') form.requestSubmit(); } }; return (
{ if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); handleAction(e); } }} className={`flex items-center justify-center gap-2 cursor-pointer transition ${className || ''} ${disabled ? 'opacity-50 cursor-not-allowed' : ''}`} title={title} > {Icon && } {children} {type === 'submit' && }
); }); // FACTORISATION : Logique métier d'accès aux panneaux const { visiblePanels } = window.getPanelAccessRights ? window.getPanelAccessRights(data.panneaux || [], myClientData.id) : { visiblePanels:[] }; const deliveries = visiblePanels.filter(c => c.physicalPanels > 0); const activeDeliveriesList = deliveries.filter(c => c.shipping_status !== 'Livré'); const historyDeliveriesList = deliveries.filter(c => c.shipping_status === 'Livré'); useEffect(() => { const url = new URL(window.location); const confirmDelivery = url.searchParams.get('confirm_delivery'); if (confirmDelivery) { setDeliveryConfirmModal(confirmDelivery); url.searchParams.delete('confirm_delivery'); window.history.replaceState({}, '', url); } }, []); const handleConfirmDelivery = async () => { if (!deliveryConfirmModal) return; const d = await window.apiFetch('panneaux/shipping', { body: { id: deliveryConfirmModal, shipping_status: 'Livré' }, setLoading: setIsSaving, successMessage: "Réception confirmée, merci !" }); if (d) { refreshData(); setDeliveryConfirmModal(null); } }; const DeliveryCard = ({ c, isHistory }) => { const isShipped = c.shipping_status === 'Expédié'; const isWaiting = c.shipping_status === 'Validation en cours' || c.shipping_status === "Attente d'impression"; const displayStatus = c.shipping_status === "Attente d'impression" ? "Validée (en attente d'impression)" : c.shipping_status; return (
{isHistory ? : }

{c.name}

{c.shippingAddress || c.location}

Quantité

{c.physicalPanels}

Statut de la commande

{c.tracking_number && (

N° de suivi : {c.tracking_number}

{c.tracking_link && ( Suivre mon colis sur le site du transporteur )}
)} {isWaiting && (

Votre commande est en cours de traitement par nos équipes. Vous recevrez un lien de suivi dès l'expédition.

)}
{isShipped && !isHistory && ( )}
); }; return (

Livraisons

Suivi de vos commandes de panneaux physiques sur eco-panneau.fr.

{deliveries.length === 0 ? (

Aucune livraison

Aucune commande de panneaux physiques associée à vos chantiers.

) : ( <> {activeDeliveriesList.length > 0 && (

En cours ({activeDeliveriesList.length})

{/* 3 colonnes décalées à 2xl */}
{activeDeliveriesList.map(c => )}
)} {historyDeliveriesList.length > 0 && (

Historique ({historyDeliveriesList.length})

{/* 3 colonnes décalées à 2xl */}
{historyDeliveriesList.map(c => )}
)} )} {/* MODALE DE CONFIRMATION DE LIVRAISON */} {deliveryConfirmModal && Modal && ( setDeliveryConfirmModal(null)} preventClose={isSaving}>

{PackageIcon && } Colis livré ?

En confirmant, vous indiquez avoir bien reçu l'intégralité de votre commande de panneaux physiques pour ce chantier.

)}
); }; /* EOF ========== [_www/_react/_clients_panneaux.jsx] */