From 42693e765627bcb7ecdb392d063b24fdc12a9a01 Mon Sep 17 00:00:00 2001 From: jason Date: Sun, 8 Mar 2026 23:22:24 -0500 Subject: [PATCH] Redesign: Compact photo gallery with modern info layout --- client/src/pages/DogDetail.jsx | 328 ++++++++++++++++++++++----------- 1 file changed, 224 insertions(+), 104 deletions(-) diff --git a/client/src/pages/DogDetail.jsx b/client/src/pages/DogDetail.jsx index 9688c63..7c1f91c 100644 --- a/client/src/pages/DogDetail.jsx +++ b/client/src/pages/DogDetail.jsx @@ -1,15 +1,17 @@ import { useEffect, useState, useRef } from 'react' -import { useParams, Link } from 'react-router-dom' -import { Dog, GitBranch, Edit, Upload, Trash2 } from 'lucide-react' +import { useParams, Link, useNavigate } from 'react-router-dom' +import { Dog, GitBranch, Edit, Upload, Trash2, ArrowLeft, Calendar, Hash, Award } from 'lucide-react' import axios from 'axios' import DogForm from '../components/DogForm' function DogDetail() { const { id } = useParams() + const navigate = useNavigate() const [dog, setDog] = useState(null) const [loading, setLoading] = useState(true) const [showEditModal, setShowEditModal] = useState(false) const [uploading, setUploading] = useState(false) + const [selectedPhoto, setSelectedPhoto] = useState(0) const fileInputRef = useRef(null) useEffect(() => { @@ -55,12 +57,32 @@ function DogDetail() { try { await axios.delete(`/api/dogs/${id}/photos/${photoIndex}`) fetchDog() + if (selectedPhoto >= photoIndex && selectedPhoto > 0) { + setSelectedPhoto(selectedPhoto - 1) + } } catch (error) { console.error('Error deleting photo:', error) alert('Failed to delete photo') } } + const calculateAge = (birthDate) => { + if (!birthDate) return null + const today = new Date() + const birth = new Date(birthDate) + let years = today.getFullYear() - birth.getFullYear() + let months = today.getMonth() - birth.getMonth() + + if (months < 0) { + years-- + months += 12 + } + + if (years === 0) return `${months} month${months !== 1 ? 's' : ''}` + if (months === 0) return `${years} year${years !== 1 ? 's' : ''}` + return `${years}y ${months}m` + } + if (loading) { return
Loading...
} @@ -70,64 +92,51 @@ function DogDetail() { } return ( -
-
-

{dog.name}

-
- - - View Pedigree +
+ {/* Header */} +
+ +
+

{dog.name}

+
+ {dog.breed} + + {dog.sex === 'male' ? 'Male ♂' : 'Female ♀'} + {dog.birth_date && ( + <> + + {calculateAge(dog.birth_date)} + + )} +
+
+
+ + + Pedigree -
-
-
-

Basic Information

-
-
- Breed: {dog.breed} -
-
- Sex: {dog.sex === 'male' ? 'Male ♂' : 'Female ♀'} -
- {dog.birth_date && ( -
- Birth Date: {new Date(dog.birth_date).toLocaleDateString()} -
- )} - {dog.color && ( -
- Color: {dog.color} -
- )} - {dog.registration_number && ( -
- Registration: {dog.registration_number} -
- )} - {dog.microchip && ( -
- Microchip: {dog.microchip} -
- )} -
-
- -
-
-

Photos

+
+ {/* Photo Section - Compact */} +
+
+

Photos

+ {dog.photo_urls && dog.photo_urls.length > 0 ? ( -
- {dog.photo_urls.map((url, index) => ( -
- {`${dog.name} - + <> + {/* Main Photo */} +
+ {dog.name} + +
+ + {/* Thumbnail Strip */} + {dog.photo_urls.length > 1 && ( +
+ {dog.photo_urls.map((url, index) => ( + {`${dog.name} setSelectedPhoto(index)} + style={{ + width: '60px', + height: '60px', + objectFit: 'cover', + borderRadius: 'var(--radius-sm)', + cursor: 'pointer', + border: selectedPhoto === index ? '2px solid var(--primary)' : '1px solid var(--border)', + opacity: selectedPhoto === index ? 1 : 0.6, + transition: 'all 0.2s' + }} + /> + ))}
- ))} -
+ )} + ) : ( -
- -

No photos uploaded

+
+ +

No photos

)}
+ + {/* Info Section */} +
+
+

Details

+ +
+
+ Breed + {dog.breed} +
+ +
+ Sex + {dog.sex === 'male' ? 'Male ♂' : 'Female ♀'} +
+ + {dog.birth_date && ( +
+ Birth Date + + {new Date(dog.birth_date).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })} + ({calculateAge(dog.birth_date)}) + +
+ )} + + {dog.color && ( +
+ Color + {dog.color} +
+ )} + + {dog.registration_number && ( +
+ Registration + {dog.registration_number} +
+ )} + + {dog.microchip && ( +
+ Microchip + {dog.microchip} +
+ )} +
+
+ + {/* Parents */} +
+

Pedigree

+
+
+
Sire
+ {dog.sire ? ( + + {dog.sire.name} + + ) : ( + Unknown + )} +
+
+
Dam
+ {dog.dam ? ( + + {dog.dam.name} + + ) : ( + Unknown + )} +
+
+
+
+ {/* Notes */} {dog.notes && ( -
-

Notes

-

{dog.notes}

+
+

Notes

+

{dog.notes}

)} -
-

Parents

-
-
-

Sire (Father)

- {dog.sire ? ( - {dog.sire.name} - ) : ( -

Unknown

- )} -
-
-

Dam (Mother)

- {dog.dam ? ( - {dog.dam.name} - ) : ( -

Unknown

- )} -
-
-
- + {/* Offspring */} {dog.offspring && dog.offspring.length > 0 && ( -
-

Offspring ({dog.offspring.length})

-
+
+

Offspring ({dog.offspring.length})

+
{dog.offspring.map(child => ( - - {child.name} - {child.sex === 'male' ? '♂' : '♀'} + { + e.currentTarget.style.borderColor = 'var(--primary)' + e.currentTarget.style.background = 'var(--bg-tertiary)' + }} + onMouseLeave={(e) => { + e.currentTarget.style.borderColor = 'var(--border)' + e.currentTarget.style.background = 'var(--bg-primary)' + }} + > + {child.name} + {child.sex === 'male' ? '♂' : '♀'} ))}