Files
ferrovia-panel/src/pages/materiel-roulant/MaterielCard.js
T
mathis 37ab831aac RELEASE V.1.2.1 - Voici les fonctionnalités :
->Refonte visuelle + Prise en charge du mode sombre par l'ensemble des composants.
2026-09-22 22:14:21 +02:00

162 lines
5.9 KiB
JavaScript

import React, { useState, useEffect, useRef } from 'react';
export default function MaterielCard({ materiel, onEdit, onDelete }) {
const [index, setIndex] = useState(0);
const imgContainerRef = useRef(null);
const imgRef = useRef(null);
const imgs = React.useMemo(() => {
if (!materiel) return [];
if (Array.isArray(materiel.images) && materiel.images.length > 0) return materiel.images;
if (Array.isArray(materiel.image_url) && materiel.image_url.length > 0) return materiel.image_url;
if (typeof materiel.image_url === 'string' && materiel.image_url) return [materiel.image_url];
return [];
}, [materiel]);
const [offsetX, setOffsetX] = useState(0);
const [maxOffset, setMaxOffset] = useState(0);
const recomputeSizes = () => {
const c = imgContainerRef.current;
const i = imgRef.current;
const cw = c ? c.clientWidth : 0;
const iw = (i && i.naturalWidth && i.naturalHeight && c) ? Math.round(i.naturalWidth * (c.clientHeight / i.naturalHeight)) : (i ? i.clientWidth : 0);
const computedMaxOffset = Math.min(0, Math.floor(cw - iw));
setMaxOffset(computedMaxOffset);
setOffsetX(prev => Math.max(computedMaxOffset, Math.min(0, prev)));
};
useEffect(() => {
recomputeSizes();
const onResize = () => recomputeSizes();
window.addEventListener('resize', onResize);
return () => window.removeEventListener('resize', onResize);
}, [index, imgs.length]);
const next = (e) => {
e && e.stopPropagation();
if (!imgs || imgs.length === 0) return;
if (imgs.length > 1) {
setIndex((i) => (i + 1) % imgs.length);
} else {
const c = imgContainerRef.current;
if (!c) return;
const step = Math.max(80, Math.floor(c.clientWidth / 2));
setOffsetX(prev => Math.max(maxOffset, prev - step));
}
};
const prev = (e) => {
e && e.stopPropagation();
if (!imgs || imgs.length === 0) return;
if (imgs.length > 1) {
setIndex((i) => (i - 1 + imgs.length) % imgs.length);
} else {
const c = imgContainerRef.current;
if (!c) return;
const step = Math.max(80, Math.floor(c.clientWidth / 2));
setOffsetX(prev => Math.min(0, prev + step));
}
};
const current = imgs && imgs.length > 0 ? imgs[index] : null;
return (
<div
className="materiel-card-modern"
style={{
padding: '16px',
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
height: '100%'
}}
>
<div>
<div style={{ position: 'relative' }}>
<div
ref={imgContainerRef}
style={{
width: '100%',
height: 140,
borderRadius: 10,
marginBottom: 12,
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-start',
overflow: 'hidden',
position: 'relative',
background: '#f1f5f9'
}}
>
{current ? (
<img
ref={imgRef}
src={current}
alt={materiel?.nom || ''}
onLoad={() => recomputeSizes()}
style={{ height: '100%', width: 'auto', objectFit: 'contain', objectPosition: 'left center', display: 'block', transform: `translateX(${offsetX}px)` }}
/>
) : (
<div style={{ width: '100%', height: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', color: '#94a3b8', gap: 4 }}>
<wcs-mat-icon icon="train" size="m"></wcs-mat-icon>
<span style={{ fontSize: 12 }}>Sans image</span>
</div>
)}
{(() => {
const isMulti = imgs.length > 1;
const canPrev = isMulti ? index > 0 : offsetX < 0;
const canNext = isMulti ? index < imgs.length - 1 : offsetX > maxOffset;
return (
<>
{canPrev && (
<button
aria-label="Précédent"
onClick={prev}
className="materiel-card-nav-btn prev"
>
<wcs-mat-icon icon="chevron_left" size="s"></wcs-mat-icon>
</button>
)}
{canNext && (
<button
aria-label="Suivant"
onClick={next}
className="materiel-card-nav-btn next"
>
<wcs-mat-icon icon="chevron_right" size="s"></wcs-mat-icon>
</button>
)}
</>
);
})()}
</div>
<div style={{ marginBottom: 10 }}>
<div style={{ fontWeight: 800, fontSize: 16, color: '#0f172a' }}>{materiel?.nom ?? '-'}</div>
<div style={{ fontSize: 12, color: '#64748b', fontWeight: 600 }}>{materiel?.nom_technique ?? '-'}</div>
</div>
<div style={{ fontSize: 13, color: '#334155', display: 'flex', flexDirection: 'column', gap: 4, marginBottom: 12 }}>
<div>Capacité : <strong>{materiel?.capacite ? `${materiel.capacite} places` : '-'}</strong></div>
<div>Type de train : <strong>{materiel?.type_train ?? '-'}</strong></div>
<div>N° de série : <strong>{materiel?.numero_serie ?? '-'}</strong></div>
</div>
</div>
<div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end', marginTop: 12, borderTop: '1px solid #f1f5f9', paddingTop: 10 }}>
<wcs-button className="wcs-secondary" onClick={onEdit}>
<wcs-mat-icon icon="edit" size="s"></wcs-mat-icon>
Modifier
</wcs-button>
<wcs-button className="wcs-danger" onClick={onDelete}>
<wcs-mat-icon icon="delete" size="s"></wcs-mat-icon>
Supprimer
</wcs-button>
</div>
</div>
</div>
);
}