MIGRATION VERS GITEA

This commit is contained in:
mathis committed 2026-08-25 01:23:13 +02:00
commit 5f9d991c15
333 files changed
+108514

No files matched your search

+169
View File
@@ -0,0 +1,169 @@
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="card materiel-card"
style={{
border: '1px solid #e0e0e0',
borderRadius: 8,
padding: 12,
boxShadow: '0 1px 2px rgba(0,0,0,0.04)',
background: '#fff',
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
height: '100%'
}}
>
<div>
<div style={{ position: 'relative' }}>
<div ref={imgContainerRef} style={{ width: '100%', height: 140, borderRadius: 6, marginBottom: 12, display: 'flex', alignItems: 'center', justifyContent: 'flex-start', overflow: 'hidden', position: 'relative' }}>
{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%', background: '#f5f5f5', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#999', borderRadius: 6 }}>No image</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}
style={{
position: 'absolute',
left: 6,
top: '50%',
transform: 'translateY(-50%)',
background: 'rgba(0,0,0,0.55)',
color: '#fff',
border: 'none',
borderRadius: 6,
padding: '8px 10px',
cursor: 'pointer',
zIndex: 3,
boxShadow: '0 2px 6px rgba(0,0,0,0.2)'
}}
>
‹
</button>
)}
{canNext && (
<button
aria-label="Suivant"
onClick={next}
style={{
position: 'absolute',
right: 6,
top: '50%',
transform: 'translateY(-50%)',
background: 'rgba(0,0,0,0.55)',
color: '#fff',
border: 'none',
borderRadius: 6,
padding: '8px 10px',
cursor: 'pointer',
zIndex: 3,
boxShadow: '0 2px 6px rgba(0,0,0,0.2)'
}}
>
›
</button>
)}
</>
);
})()}
</div>
<div style={{ marginBottom: 8 }}>
<div style={{ fontWeight: 600, fontSize: 15 }}>{materiel?.nom ?? '-'}</div>
<div style={{ fontSize: 12, color: '#666' }}>{materiel?.nom_technique ?? '-'}</div>
</div>
<div style={{ fontSize: 13, color: '#333', marginBottom: 8 }}>
<div>Capacité: <strong>{materiel?.capacite ?? '-'}</strong></div>
<div>Type: <strong>{materiel?.type_train ?? '-'}</strong></div>
<div>Numéro série: <strong>{materiel?.numero_serie ?? '-'}</strong></div>
</div>
</div>
<div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end', marginTop: 8 }}>
<wcs-button onClick={onEdit}>Modifier</wcs-button>
<wcs-button className="wcs-danger" onClick={onDelete}>Supprimer</wcs-button>
</div>
</div>
</div>
);
}