FEAT-6: wire tab content into animal detail (Fotos/Gesundheit/Gewicht) + profile photo in header + tab styles

This commit is contained in:
2026-06-06 00:28:31 +02:00
parent 87250ba007
commit cd4328cef8
3 changed files with 169 additions and 1 deletions

View File

@@ -0,0 +1,20 @@
/**
* FEAT-6: Profilfoto im Detail-Kopf — erstes Foto nach sortOrder.
* Selbstständig ladend; rendert nichts, solange es keine Fotos gibt
* (oder die Foto-Endpunkte noch nicht live sind).
*/
import { listGerbilPhotos, photoSrc, profilePhoto } from '../api/photos'
import { useApi } from '../hooks/useApi'
export default function GerbilProfilePhoto({ gerbilId }: { gerbilId: string }) {
const photos = useApi(() => listGerbilPhotos(gerbilId), [gerbilId])
const profile = profilePhoto(photos.data ?? [])
if (!profile) return null
return (
<img
className="profile-photo"
src={photoSrc(profile)}
alt={profile.caption ?? profile.fileName}
/>
)
}

View File

@@ -602,3 +602,143 @@ textarea {
color: #7a5a14;
margin-left: 0.4rem;
}
/* ── FEAT-6 (Oscar): Tier-Detail-Tabs — Gesundheit / Gewicht / Fotos ── */
/* Eintragskarten (Gesundheit, Gewichtsverlauf) */
.record-card {
padding: 0.75rem 1rem;
border: 1px solid var(--color-border);
border-radius: 0.6rem;
background: var(--color-surface);
}
.record-card__head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.5rem;
}
.record-card__date {
font-weight: 600;
}
.record-card__body {
margin: 0.4rem 0;
white-space: pre-wrap;
}
.record-card__actions {
display: flex;
gap: 0.5rem;
margin-top: 0.5rem;
}
.record-card--row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
}
/* Gewicht-Schnellerfassung: großes, daumenfreundliches Eingabefeld */
.weight-quick-add {
margin-bottom: 1.25rem;
}
.weight-quick-add__row {
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
align-items: flex-end;
}
.input--xl {
font-size: 1.5rem;
min-height: 56px;
max-width: 9rem;
text-align: center;
}
/* Gewichts-Diagramm (eigenes SVG) */
.line-chart {
width: 100%;
height: auto;
max-width: 40rem;
margin: 0.5rem 0 1.25rem;
}
.line-chart__axis {
stroke: var(--color-text-muted);
stroke-width: 1;
}
.line-chart__grid {
stroke: var(--color-border);
stroke-width: 1;
stroke-dasharray: 3 3;
}
.line-chart__label {
fill: var(--color-text-muted);
font-size: 12px;
}
.line-chart__line {
stroke: var(--color-accent);
stroke-width: 2;
}
.line-chart__dot {
fill: var(--color-accent);
}
/* Foto-Galerie */
.photo-grid {
list-style: none;
margin: 1rem 0 0;
padding: 0;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
gap: 0.75rem;
}
.photo-card {
border: 1px solid var(--color-border);
border-radius: 0.6rem;
background: var(--color-surface);
overflow: hidden;
display: flex;
flex-direction: column;
}
.photo-card img {
width: 100%;
aspect-ratio: 1;
object-fit: cover;
display: block;
}
.photo-card__bar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.4rem;
padding: 0.4rem 0.6rem;
}
.photo-card__caption {
font-size: 0.8rem;
color: var(--color-text-muted);
overflow-wrap: anywhere;
}
/* Profilfoto im Detail-Kopf */
.profile-photo {
width: 56px;
height: 56px;
border-radius: 50%;
object-fit: cover;
border: 2px solid var(--color-border);
}

View File

@@ -6,6 +6,11 @@ import { listColorVarieties, listContacts, listEnclosures, listLitters } from '.
import { useApi } from '../hooks/useApi'
import { formatDate, genderLabel, statusLabel } from '../format/labels'
import { fromDisplayString, genotypeToFarbschlag, toDisplayString } from '../genetics'
// FEAT-6 (Oscar): Tab-Inhalte + Profilfoto
import GerbilHealthTab from '../components/GerbilHealthTab'
import GerbilPhotosTab from '../components/GerbilPhotosTab'
import GerbilProfilePhoto from '../components/GerbilProfilePhoto'
import GerbilWeightTab from '../components/GerbilWeightTab'
type DetailTab = 'photos' | 'health' | 'weight'
@@ -79,6 +84,7 @@ export default function GerbilDetailPage() {
<section className="page">
<header className="page-head">
<div>
<GerbilProfilePhoto gerbilId={g.id} />
<h2>{g.name}</h2>
<span className={`badge badge--${g.status.toLowerCase()}`}>{statusLabel(g.status)}</span>
</div>
@@ -158,7 +164,9 @@ export default function GerbilDetailPage() {
))}
</div>
<div className="tab-panel" role="tabpanel">
<p className="muted">{t.detail.tabPlaceholder}</p>
{tab === 'photos' && <GerbilPhotosTab gerbilId={g.id} />}
{tab === 'health' && <GerbilHealthTab gerbilId={g.id} />}
{tab === 'weight' && <GerbilWeightTab gerbilId={g.id} />}
</div>
</section>
)