diff --git a/.claude/launch.json b/.claude/launch.json
new file mode 100644
index 0000000..d9818d3
--- /dev/null
+++ b/.claude/launch.json
@@ -0,0 +1,12 @@
+{
+ "version": "0.0.1",
+ "configurations": [
+ {
+ "name": "client",
+ "runtimeExecutable": "npm",
+ "runtimeArgs": ["run", "dev"],
+ "cwd": "client",
+ "port": 5173
+ }
+ ]
+}
diff --git a/client/src/App.css b/client/src/App.css
index 227c5b4..6d90de8 100644
--- a/client/src/App.css
+++ b/client/src/App.css
@@ -139,14 +139,22 @@
height: 4rem;
}
- .nav-links {
+ .navbar .container {
+ flex-wrap: wrap;
gap: 0.25rem;
}
-
+
+ .nav-links {
+ gap: 0.25rem;
+ max-width: 100%;
+ overflow-x: auto;
+ -webkit-overflow-scrolling: touch;
+ }
+
.nav-link span {
display: none;
}
-
+
.nav-link {
padding: 0.625rem;
}
diff --git a/client/src/components/HealthRecordForm.jsx b/client/src/components/HealthRecordForm.jsx
index 670a925..12079c1 100644
--- a/client/src/components/HealthRecordForm.jsx
+++ b/client/src/components/HealthRecordForm.jsx
@@ -100,6 +100,14 @@ export default function HealthRecordForm({ dogId, dogBirthDate, record, onClose,
test_type: v === 'ofa_clearance' ? (f.test_type || 'hip_ofa') : f.test_type,
}))
+ // Live warning while typing — validate() still hard-blocks on submit.
+ const ageWarning = (() => {
+ if (!isOFA || !ofaTest?.minAgeMonths || !dogBirthDate || !form.test_date) return null
+ const months = (new Date(form.test_date) - new Date(dogBirthDate)) / (1000 * 60 * 60 * 24 * 30.44)
+ if (months < 0 || months >= ofaTest.minAgeMonths) return null
+ return `Dog was ~${Math.floor(months)} months old on this date — final ${ofaTest.label} certification requires ${ofaTest.minAgeMonths} months.`
+ })()
+
const validate = () => {
if (!form.test_date) return 'A date is required.'
if (form.test_date > today()) return 'Date cannot be in the future.'
@@ -250,6 +258,9 @@ export default function HealthRecordForm({ dogId, dogBirthDate, record, onClose,
set('test_date', e.target.value)} />
+ {ageWarning && (
+ {ageWarning}
+ )}
{ofaTest?.recurs ? (
diff --git a/client/src/components/LitterForm.jsx b/client/src/components/LitterForm.jsx
index 7576dd8..b1d2ff7 100644
--- a/client/src/components/LitterForm.jsx
+++ b/client/src/components/LitterForm.jsx
@@ -116,6 +116,11 @@ function LitterForm({ litter, prefill, onClose, onSave }) {
))}
+ {!!litter && (
+
+ Parents can't be changed after the litter is created.
+
+ )}
diff --git a/client/src/pages/BreedingCalendar.jsx b/client/src/pages/BreedingCalendar.jsx
index 5cb5066..a03fcef 100644
--- a/client/src/pages/BreedingCalendar.jsx
+++ b/client/src/pages/BreedingCalendar.jsx
@@ -534,7 +534,7 @@ export default function BreedingCalendar() {
{/* Day headers */}
-
+
{DAY_NAMES.map(d => (
{d}
))}
@@ -544,7 +544,7 @@ export default function BreedingCalendar() {
{loading ? (
Loading calendar…
) : (
-
+
{Array.from({ length: rows * 7 }).map((_, idx) => {
const dayNum = idx - startPad + 1
const isValid = dayNum >= 1 && dayNum <= lastDay.getDate()
diff --git a/client/src/pages/DogDetail.jsx b/client/src/pages/DogDetail.jsx
index cfdc17f..cffdef9 100644
--- a/client/src/pages/DogDetail.jsx
+++ b/client/src/pages/DogDetail.jsx
@@ -126,7 +126,7 @@ function DogDetail() {
return (
{/* Header */}
-
+
@@ -160,7 +160,7 @@ function DogDetail() {
-
+
{/* Photo Section */}
diff --git a/client/src/pages/LitterDetail.jsx b/client/src/pages/LitterDetail.jsx
index a912080..b890095 100644
--- a/client/src/pages/LitterDetail.jsx
+++ b/client/src/pages/LitterDetail.jsx
@@ -272,6 +272,7 @@ function LitterDetail() {
const [addMode, setAddMode] = useState('existing')
const [error, setError] = useState('')
const [saving, setSaving] = useState(false)
+ const [puppySort, setPuppySort] = useState('name')
const toast = useToast()
const confirm = useConfirm()
@@ -351,6 +352,12 @@ function LitterDetail() {
const puppyCount = litter.puppies?.length ?? 0
+ const sortedPuppies = [...(litter.puppies || [])].sort((a, b) => {
+ if (puppySort === 'sex') return (a.sex || '').localeCompare(b.sex || '') || (a.name || '').localeCompare(b.name || '')
+ if (puppySort === 'dob') return (a.date_of_birth || '').localeCompare(b.date_of_birth || '') || (a.name || '').localeCompare(b.name || '')
+ return (a.name || '').localeCompare(b.name || '')
+ })
+
return (
{/* Header */}
@@ -405,12 +412,27 @@ function LitterDetail() {
)}
{/* Puppies section */}
-
+
Puppies
-
+
+ {puppyCount > 1 && (
+
+ )}
+
+
{puppyCount === 0 ? (
@@ -419,8 +441,8 @@ function LitterDetail() {
No puppies linked yet. Add puppies to this litter.
) : (
-
- {litter.puppies.map(puppy => (
+
+ {sortedPuppies.map(puppy => (