Merge feature/stammbaum-avatar (STAMMBAUM-AVATAR): Stammbaum-Knoten konsumieren profilePhotoUrl (Foto-Avatar mit imgError-Fallback auf GerbilIcon)
This commit is contained in:
@@ -56,6 +56,8 @@ export interface Gerbil {
|
||||
isResident?: boolean
|
||||
/** FORM-FIELDS-1: true=gehörlos, false=hörend, null=unbekannt. */
|
||||
isDeaf?: boolean | null
|
||||
/** STAMMBAUM-AVATAR: URL des Profilfotos (erstes Foto nach sortOrder); null = kein Foto. API-relativ oder absolut. */
|
||||
profilePhotoUrl?: string | null
|
||||
}
|
||||
|
||||
/** Payload for POST /gerbils. */
|
||||
|
||||
@@ -21,7 +21,7 @@ import { Link, useNavigate, useParams } from 'react-router-dom'
|
||||
import Tree from 'react-d3-tree'
|
||||
import type { CustomNodeElementProps, Point, RawNodeDatum } from 'react-d3-tree'
|
||||
import { de } from '../strings/de'
|
||||
import { ApiError } from '../api/client'
|
||||
import { API_BASE_URL, ApiError } from '../api/client'
|
||||
import { listLitters } from '../api/litters'
|
||||
import { listColorVarieties } from '../api/lookups'
|
||||
import { getInbreedingCoefficient } from '../api/pedigree'
|
||||
@@ -381,6 +381,14 @@ function PedigreeCard({
|
||||
const g = node.gerbil
|
||||
const chip = farbschlag ? chipColorFor(farbschlag) : null
|
||||
const dob = g.dateOfBirth ? formatDate(g.dateOfBirth) : null
|
||||
const [imgError, setImgError] = useState(false)
|
||||
useEffect(() => setImgError(false), [g.id])
|
||||
const photoUrl =
|
||||
!imgError && g.profilePhotoUrl
|
||||
? /^(https?:|data:|\/\/)/.test(g.profilePhotoUrl)
|
||||
? g.profilePhotoUrl
|
||||
: `${API_BASE_URL}${g.profilePhotoUrl}`
|
||||
: null
|
||||
return (
|
||||
<div
|
||||
className={isRoot ? 'pedigree-card pedigree-card--root' : 'pedigree-card'}
|
||||
@@ -388,9 +396,17 @@ function PedigreeCard({
|
||||
role={isRoot ? undefined : 'button'}
|
||||
title={isRoot ? undefined : t.tapHint}
|
||||
>
|
||||
{/* Foto-Platzhalter — echte Fotos kommen mit FEAT-6. */}
|
||||
<div className="pedigree-card__photo" aria-hidden="true">
|
||||
<GerbilIcon size="1.6rem" />
|
||||
<div className="pedigree-card__photo" aria-hidden={!photoUrl || undefined}>
|
||||
{photoUrl ? (
|
||||
<img
|
||||
src={photoUrl}
|
||||
alt={g.name || ''}
|
||||
className="pedigree-card__avatar"
|
||||
onError={() => setImgError(true)}
|
||||
/>
|
||||
) : (
|
||||
<GerbilIcon size="1.6rem" aria-hidden="true" />
|
||||
)}
|
||||
</div>
|
||||
<div className="pedigree-card__body">
|
||||
<div className="pedigree-card__name">
|
||||
|
||||
@@ -236,6 +236,7 @@
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
background: var(--color-accent-soft);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -243,6 +244,13 @@
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
.pedigree-card__avatar {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.pedigree-card__body {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
|
||||
Reference in New Issue
Block a user