feature/ui-redesign #3

Merged
jason merged 3 commits from feature/ui-redesign into master 2026-03-08 23:33:10 -05:00
Showing only changes of commit a56c403af4 - Show all commits

View File

@@ -1,6 +1,6 @@
import { useEffect, useState } from 'react'
import { Link } from 'react-router-dom'
import { Dog, Activity, Heart, AlertCircle } from 'lucide-react'
import { Dog, Activity, Heart, Calendar, Hash, ArrowRight } from 'lucide-react'
import axios from 'axios'
function Dashboard() {
@@ -35,7 +35,7 @@ function Dashboard() {
activeHeatCycles: heatCyclesRes.data.length
})
setRecentDogs(dogs.slice(0, 6))
setRecentDogs(dogs.slice(0, 8))
setLoading(false)
} catch (error) {
console.error('Error fetching dashboard data:', error)
@@ -43,65 +43,203 @@ function Dashboard() {
}
}
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}mo`
if (months === 0) return `${years}y`
return `${years}y ${months}mo`
}
if (loading) {
return <div className="container loading">Loading dashboard...</div>
}
return (
<div className="container">
<div className="container" style={{ paddingTop: '2rem', paddingBottom: '3rem' }}>
<h1 style={{ marginBottom: '2rem' }}>Dashboard</h1>
<div className="grid grid-3" style={{ marginBottom: '3rem' }}>
<div className="card" style={{ textAlign: 'center' }}>
<Dog size={48} style={{ color: 'var(--primary)', margin: '0 auto 1rem' }} />
<h3 style={{ fontSize: '2rem', marginBottom: '0.5rem' }}>{stats.totalDogs}</h3>
<p style={{ color: 'var(--text-secondary)' }}>Total Dogs</p>
<p style={{ fontSize: '0.875rem', marginTop: '0.5rem' }}>
{stats.males} Males {stats.females} Females
</p>
{/* Stats Grid */}
<div className="grid grid-4" style={{ marginBottom: '3rem' }}>
<div className="card stat-card">
<div className="stat-icon" style={{ background: 'linear-gradient(135deg, var(--primary), var(--accent))' }}>
<Dog size={24} color="white" />
</div>
<div className="stat-value">{stats.totalDogs}</div>
<div className="stat-label">Total Dogs</div>
<div style={{ marginTop: '0.75rem', fontSize: '0.875rem', color: 'var(--text-secondary)' }}>
{stats.males} · {stats.females}
</div>
</div>
<div className="card" style={{ textAlign: 'center' }}>
<Activity size={48} style={{ color: 'var(--success)', margin: '0 auto 1rem' }} />
<h3 style={{ fontSize: '2rem', marginBottom: '0.5rem' }}>{stats.totalLitters}</h3>
<p style={{ color: 'var(--text-secondary)' }}>Total Litters</p>
<div className="card stat-card">
<div className="stat-icon" style={{ background: 'linear-gradient(135deg, var(--success), #059669)' }}>
<Activity size={24} color="white" />
</div>
<div className="stat-value">{stats.totalLitters}</div>
<div className="stat-label">Total Litters</div>
</div>
<div className="card" style={{ textAlign: 'center' }}>
<Heart size={48} style={{ color: 'var(--danger)', margin: '0 auto 1rem' }} />
<h3 style={{ fontSize: '2rem', marginBottom: '0.5rem' }}>{stats.activeHeatCycles}</h3>
<p style={{ color: 'var(--text-secondary)' }}>Active Heat Cycles</p>
<div className="card stat-card">
<div className="stat-icon" style={{ background: 'linear-gradient(135deg, var(--danger), #dc2626)' }}>
<Heart size={24} color="white" />
</div>
<div className="stat-value">{stats.activeHeatCycles}</div>
<div className="stat-label">Active Heat Cycles</div>
</div>
<Link to="/dogs" className="card stat-card" style={{ textDecoration: 'none', cursor: 'pointer', transition: 'var(--transition)' }}>
<div className="stat-icon" style={{ background: 'var(--bg-tertiary)' }}>
<ArrowRight size={24} color="var(--primary)" />
</div>
<div style={{ color: 'var(--text-primary)', fontWeight: 600, fontSize: '1.125rem', marginTop: '0.5rem' }}>View All Dogs</div>
<div className="stat-label">Manage Collection</div>
</Link>
</div>
{/* Recent Dogs Section */}
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1.5rem' }}>
<h2>Recent Dogs</h2>
<Link to="/dogs" className="btn btn-primary">View All</Link>
<Link to="/dogs" className="btn btn-primary">
View All
<ArrowRight size={16} />
</Link>
</div>
{recentDogs.length === 0 ? (
<div className="card" style={{ textAlign: 'center', padding: '3rem' }}>
<AlertCircle size={48} style={{ color: 'var(--text-secondary)', margin: '0 auto 1rem' }} />
<h3>No dogs registered yet</h3>
<p style={{ color: 'var(--text-secondary)', marginBottom: '1.5rem' }}>Start by adding your first dog to the system</p>
<Link to="/dogs" className="btn btn-primary">Add Dog</Link>
<div className="card" style={{ textAlign: 'center', padding: '4rem 2rem' }}>
<Dog size={64} style={{ color: 'var(--text-muted)', margin: '0 auto 1rem', opacity: 0.5 }} />
<h3 style={{ marginBottom: '0.5rem' }}>No dogs registered yet</h3>
<p style={{ color: 'var(--text-secondary)', marginBottom: '2rem' }}>Start building your kennel management system</p>
<Link to="/dogs" className="btn btn-primary">Add Your First Dog</Link>
</div>
) : (
<div className="grid grid-3">
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(320px, 1fr))', gap: '1rem' }}>
{recentDogs.map(dog => (
<Link key={dog.id} to={`/dogs/${dog.id}`} className="card" style={{ textDecoration: 'none', color: 'inherit' }}>
<div style={{ aspectRatio: '1', background: 'var(--bg-secondary)', borderRadius: '0.375rem', marginBottom: '1rem', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<Link
key={dog.id}
to={`/dogs/${dog.id}`}
className="card"
style={{
padding: '1rem',
textDecoration: 'none',
display: 'flex',
gap: '1rem',
alignItems: 'center',
transition: 'var(--transition)',
cursor: 'pointer'
}}
onMouseEnter={(e) => {
e.currentTarget.style.borderColor = 'var(--primary)'
e.currentTarget.style.transform = 'translateY(-2px)'
}}
onMouseLeave={(e) => {
e.currentTarget.style.borderColor = 'var(--border)'
e.currentTarget.style.transform = 'translateY(0)'
}}
>
{/* Avatar Photo */}
<div style={{
width: '80px',
height: '80px',
flexShrink: 0,
borderRadius: 'var(--radius)',
background: 'var(--bg-primary)',
border: '2px solid var(--border)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
overflow: 'hidden'
}}>
{dog.photo_urls && dog.photo_urls.length > 0 ? (
<img src={dog.photo_urls[0]} alt={dog.name} style={{ width: '100%', height: '100%', objectFit: 'cover', borderRadius: '0.375rem' }} />
<img
src={dog.photo_urls[0]}
alt={dog.name}
style={{
width: '100%',
height: '100%',
objectFit: 'cover'
}}
/>
) : (
<Dog size={48} style={{ color: 'var(--text-secondary)' }} />
<Dog size={32} style={{ color: 'var(--text-muted)', opacity: 0.5 }} />
)}
</div>
<h3>{dog.name}</h3>
<p style={{ color: 'var(--text-secondary)', fontSize: '0.875rem' }}>{dog.breed} {dog.sex}</p>
{dog.registration_number && (
<p style={{ color: 'var(--text-secondary)', fontSize: '0.75rem', marginTop: '0.25rem' }}>{dog.registration_number}</p>
)}
{/* Info Section */}
<div style={{ flex: 1, minWidth: 0 }}>
<h3 style={{
fontSize: '1.125rem',
marginBottom: '0.375rem',
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap'
}}>
{dog.name}
<span style={{
marginLeft: '0.5rem',
fontSize: '1rem',
color: dog.sex === 'male' ? 'var(--primary)' : '#ec4899'
}}>
{dog.sex === 'male' ? '♂' : '♀'}
</span>
</h3>
<div style={{
display: 'flex',
flexWrap: 'wrap',
gap: '0.75rem',
fontSize: '0.8125rem',
color: 'var(--text-secondary)',
marginBottom: '0.5rem'
}}>
<span>{dog.breed}</span>
{dog.birth_date && (
<>
<span></span>
<span style={{ display: 'flex', alignItems: 'center', gap: '0.25rem' }}>
<Calendar size={12} />
{calculateAge(dog.birth_date)}
</span>
</>
)}
</div>
{dog.registration_number && (
<div style={{
display: 'inline-flex',
alignItems: 'center',
gap: '0.25rem',
padding: '0.25rem 0.5rem',
background: 'var(--bg-primary)',
border: '1px solid var(--border)',
borderRadius: 'var(--radius-sm)',
fontSize: '0.75rem',
fontFamily: 'monospace',
color: 'var(--text-muted)'
}}>
<Hash size={10} />
{dog.registration_number}
</div>
)}
</div>
{/* Arrow Indicator */}
<div style={{
opacity: 0.5,
transition: 'var(--transition)'
}}>
<ArrowRight size={20} color="var(--text-muted)" />
</div>
</Link>
))}
</div>